mirror of https://github.com/astral-sh/ruff
[ty] Delegate truthiness inference of an enum `Literal` type to its enum-instance supertype (#21060)
This commit is contained in:
parent
e196c2ab37
commit
bf74c824eb
|
|
@ -78,6 +78,7 @@ python-version = "3.11"
|
||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
import enum
|
||||||
from typing import Literal, final
|
from typing import Literal, final
|
||||||
|
|
||||||
reveal_type(bool(1)) # revealed: Literal[True]
|
reveal_type(bool(1)) # revealed: Literal[True]
|
||||||
|
|
@ -129,13 +130,20 @@ class FinalClassWithNoLenOrBool: ...
|
||||||
|
|
||||||
reveal_type(bool(FinalClassWithNoLenOrBool())) # revealed: Literal[True]
|
reveal_type(bool(FinalClassWithNoLenOrBool())) # revealed: Literal[True]
|
||||||
|
|
||||||
def f(x: SingleElementTupleSubclass | FinalClassOverridingLenAndNotBool | FinalClassWithNoLenOrBool):
|
class EnumWithMembers(enum.Enum):
|
||||||
|
A = 1
|
||||||
|
B = 2
|
||||||
|
|
||||||
|
reveal_type(bool(EnumWithMembers.A)) # revealed: Literal[True]
|
||||||
|
|
||||||
|
def f(x: SingleElementTupleSubclass | FinalClassOverridingLenAndNotBool | FinalClassWithNoLenOrBool | Literal[EnumWithMembers.A]):
|
||||||
reveal_type(bool(x)) # revealed: Literal[True]
|
reveal_type(bool(x)) # revealed: Literal[True]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Falsy values
|
## Falsy values
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
import enum
|
||||||
from typing import final, Literal
|
from typing import final, Literal
|
||||||
|
|
||||||
reveal_type(bool(0)) # revealed: Literal[False]
|
reveal_type(bool(0)) # revealed: Literal[False]
|
||||||
|
|
@ -156,13 +164,23 @@ class FinalClassOverridingLenAndNotBool:
|
||||||
|
|
||||||
reveal_type(bool(FinalClassOverridingLenAndNotBool())) # revealed: Literal[False]
|
reveal_type(bool(FinalClassOverridingLenAndNotBool())) # revealed: Literal[False]
|
||||||
|
|
||||||
def f(x: EmptyTupleSubclass | FinalClassOverridingLenAndNotBool):
|
class EnumWithMembersOverridingBool(enum.Enum):
|
||||||
|
A = 1
|
||||||
|
B = 2
|
||||||
|
|
||||||
|
def __bool__(self) -> Literal[False]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
reveal_type(bool(EnumWithMembersOverridingBool.A)) # revealed: Literal[False]
|
||||||
|
|
||||||
|
def f(x: EmptyTupleSubclass | FinalClassOverridingLenAndNotBool | Literal[EnumWithMembersOverridingBool.A]):
|
||||||
reveal_type(bool(x)) # revealed: Literal[False]
|
reveal_type(bool(x)) # revealed: Literal[False]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ambiguous values
|
## Ambiguous values
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
import enum
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
reveal_type(bool([])) # revealed: bool
|
reveal_type(bool([])) # revealed: bool
|
||||||
|
|
@ -182,6 +200,15 @@ class NonFinalOverridingLenAndNotBool:
|
||||||
# because a subclass might override `__bool__`,
|
# because a subclass might override `__bool__`,
|
||||||
# and `__bool__` takes precedence over `__len__`
|
# and `__bool__` takes precedence over `__len__`
|
||||||
reveal_type(bool(NonFinalOverridingLenAndNotBool())) # revealed: bool
|
reveal_type(bool(NonFinalOverridingLenAndNotBool())) # revealed: bool
|
||||||
|
|
||||||
|
class EnumWithMembersOverridingBool(enum.Enum):
|
||||||
|
A = 1
|
||||||
|
B = 2
|
||||||
|
|
||||||
|
def __bool__(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
reveal_type(bool(EnumWithMembersOverridingBool.A)) # revealed: bool
|
||||||
```
|
```
|
||||||
|
|
||||||
## `__bool__` returning `NoReturn`
|
## `__bool__` returning `NoReturn`
|
||||||
|
|
|
||||||
|
|
@ -183,13 +183,11 @@ class CustomLenEnum(Enum):
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# TODO: these could be `Literal[True]`
|
reveal_type(bool(NormalEnum.NO)) # revealed: Literal[True]
|
||||||
reveal_type(bool(NormalEnum.NO)) # revealed: bool
|
reveal_type(bool(NormalEnum.YES)) # revealed: Literal[True]
|
||||||
reveal_type(bool(NormalEnum.YES)) # revealed: bool
|
|
||||||
|
|
||||||
# TODO: these could be `Literal[False]`
|
reveal_type(bool(FalsyEnum.NO)) # revealed: Literal[False]
|
||||||
reveal_type(bool(FalsyEnum.NO)) # revealed: bool
|
reveal_type(bool(FalsyEnum.YES)) # revealed: Literal[False]
|
||||||
reveal_type(bool(FalsyEnum.YES)) # revealed: bool
|
|
||||||
|
|
||||||
# All of the following must be `bool`:
|
# All of the following must be `bool`:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4692,10 +4692,10 @@ impl<'db> Type<'db> {
|
||||||
Truthiness::Ambiguous
|
Truthiness::Ambiguous
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::EnumLiteral(_) => {
|
Type::EnumLiteral(enum_type) => {
|
||||||
// We currently make no attempt to infer the precise truthiness, but it's not impossible to do so.
|
enum_type
|
||||||
// Note that custom `__bool__` or `__len__` methods on the class or superclasses affect the outcome.
|
.enum_class_instance(db)
|
||||||
Truthiness::Ambiguous
|
.try_bool_impl(db, allow_short_circuit, visitor)?
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::IntLiteral(num) => Truthiness::from(*num != 0),
|
Type::IntLiteral(num) => Truthiness::from(*num != 0),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue