Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 67f673a

Browse filesBrowse files
hauntsaninjasvalentin
authored andcommitted
Fix enum truthiness for StrEnum (#18379)
Fixes #18376 See also https://snarky.ca/unravelling-not-in-python/
1 parent 3755abb commit 67f673a
Copy full SHA for 67f673a

File tree

Expand file treeCollapse file tree

3 files changed

+91
-18
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+91
-18
lines changed

‎mypy/typeops.py

Copy file name to clipboardExpand all lines: mypy/typeops.py
+5-7Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -647,19 +647,14 @@ def _remove_redundant_union_items(items: list[Type], keep_erased: bool) -> list[
647647
return items
648648

649649

650-
def _get_type_method_ret_type(t: Type, *, name: str) -> Type | None:
651-
t = get_proper_type(t)
652-
650+
def _get_type_method_ret_type(t: ProperType, *, name: str) -> Type | None:
653651
# For Enum literals the ret_type can change based on the Enum
654652
# we need to check the type of the enum rather than the literal
655653
if isinstance(t, LiteralType) and t.is_enum_literal():
656654
t = t.fallback
657655

658656
if isinstance(t, Instance):
659657
sym = t.type.get(name)
660-
# Fallback to the metaclass for the lookup when necessary
661-
if not sym and (m := t.type.metaclass_type):
662-
sym = m.type.get(name)
663658
if sym:
664659
sym_type = get_proper_type(sym.type)
665660
if isinstance(sym_type, CallableType):
@@ -732,7 +727,10 @@ def false_only(t: Type) -> ProperType:
732727
if ret_type:
733728
if not ret_type.can_be_false:
734729
return UninhabitedType(line=t.line)
735-
elif isinstance(t, Instance) and t.type.is_final:
730+
elif isinstance(t, Instance):
731+
if t.type.is_final or t.type.is_enum:
732+
return UninhabitedType(line=t.line)
733+
elif isinstance(t, LiteralType) and t.is_enum_literal():
736734
return UninhabitedType(line=t.line)
737735

738736
new_t = copy_type(t)

‎test-data/unit/check-enum.test

Copy file name to clipboardExpand all lines: test-data/unit/check-enum.test
+83-10Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -181,27 +181,100 @@ def infer_truth(truth: Truth) -> None:
181181
[case testEnumTruthyness]
182182
# mypy: warn-unreachable
183183
import enum
184+
from typing_extensions import Literal
185+
184186
class E(enum.Enum):
185-
x = 0
186-
if not E.x:
187-
"noop"
187+
zero = 0
188+
one = 1
189+
190+
def print(s: str) -> None: ...
191+
192+
if E.zero:
193+
print("zero is true")
194+
if not E.zero:
195+
print("zero is false") # E: Statement is unreachable
196+
197+
if E.one:
198+
print("one is true")
199+
if not E.one:
200+
print("one is false") # E: Statement is unreachable
201+
202+
def main(zero: Literal[E.zero], one: Literal[E.one]) -> None:
203+
if zero:
204+
print("zero is true")
205+
if not zero:
206+
print("zero is false") # E: Statement is unreachable
207+
if one:
208+
print("one is true")
209+
if not one:
210+
print("one is false") # E: Statement is unreachable
188211
[builtins fixtures/tuple.pyi]
189-
[out]
190-
main:6: error: Statement is unreachable
191212

192213
[case testEnumTruthynessCustomDunderBool]
193214
# mypy: warn-unreachable
194215
import enum
195216
from typing_extensions import Literal
217+
196218
class E(enum.Enum):
197-
x = 0
219+
zero = 0
220+
one = 1
198221
def __bool__(self) -> Literal[False]:
199222
return False
200-
if E.x:
201-
"noop"
223+
224+
def print(s: str) -> None: ...
225+
226+
if E.zero:
227+
print("zero is true") # E: Statement is unreachable
228+
if not E.zero:
229+
print("zero is false")
230+
231+
if E.one:
232+
print("one is true") # E: Statement is unreachable
233+
if not E.one:
234+
print("one is false")
235+
236+
def main(zero: Literal[E.zero], one: Literal[E.one]) -> None:
237+
if zero:
238+
print("zero is true") # E: Statement is unreachable
239+
if not zero:
240+
print("zero is false")
241+
if one:
242+
print("one is true") # E: Statement is unreachable
243+
if not one:
244+
print("one is false")
245+
[builtins fixtures/enum.pyi]
246+
247+
[case testEnumTruthynessStrEnum]
248+
# mypy: warn-unreachable
249+
import enum
250+
from typing_extensions import Literal
251+
252+
class E(enum.StrEnum):
253+
empty = ""
254+
not_empty = "asdf"
255+
256+
def print(s: str) -> None: ...
257+
258+
if E.empty:
259+
print("empty is true")
260+
if not E.empty:
261+
print("empty is false")
262+
263+
if E.not_empty:
264+
print("not_empty is true")
265+
if not E.not_empty:
266+
print("not_empty is false")
267+
268+
def main(empty: Literal[E.empty], not_empty: Literal[E.not_empty]) -> None:
269+
if empty:
270+
print("empty is true")
271+
if not empty:
272+
print("empty is false")
273+
if not_empty:
274+
print("not_empty is true")
275+
if not not_empty:
276+
print("not_empty is false")
202277
[builtins fixtures/enum.pyi]
203-
[out]
204-
main:9: error: Statement is unreachable
205278

206279
[case testEnumUnique]
207280
import enum

‎test-data/unit/fixtures/enum.pyi

Copy file name to clipboardExpand all lines: test-data/unit/fixtures/enum.pyi
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ class tuple(Generic[T]):
1111
def __getitem__(self, x: int) -> T: pass
1212

1313
class int: pass
14-
class str: pass
14+
class str:
15+
def __len__(self) -> int: pass
16+
1517
class dict: pass
1618
class ellipsis: pass

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.