Skip to content

Commit 9666276

Browse files
committed
Use _value_ as a fallback for ellipsis Enum members
1 parent 5e9d657 commit 9666276

File tree

3 files changed

+69
-4
lines changed

3 files changed

+69
-4
lines changed

mypy/plugins/enums.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from typing import Final, TypeVar, cast
1818

1919
import mypy.plugin # To avoid circular imports.
20-
from mypy.nodes import TypeInfo
20+
from mypy.nodes import TypeInfo, Var
2121
from mypy.semanal_enum import ENUM_BASES
2222
from mypy.subtypes import is_equivalent
2323
from mypy.typeops import fixup_partial_type, make_simplified_union
@@ -87,6 +87,20 @@ def _infer_value_type_with_auto_fallback(
8787
if proper_type is None:
8888
return None
8989
proper_type = get_proper_type(fixup_partial_type(proper_type))
90+
# Enums in stubs may have ... instead of actual values. If `_value_` is annotated
91+
# (manually or inherited from IntEnum, for example), it is a more reasonable guess
92+
# than literal ellipsis type.
93+
if isinstance(proper_type, Instance) and proper_type.type.fullname in {
94+
"types.EllipsisType",
95+
"builtins.ellipsis",
96+
}:
97+
if (
98+
isinstance(ctx.type, Instance)
99+
and (value_type := ctx.type.type.get("_value_"))
100+
and isinstance(var := value_type.node, Var)
101+
):
102+
return var.type
103+
return proper_type
90104
if not (isinstance(proper_type, Instance) and proper_type.type.fullname == "enum.auto"):
91105
if is_named_instance(proper_type, "enum.member") and proper_type.args:
92106
return proper_type.args[0]

test-data/unit/check-enum.test

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -755,12 +755,10 @@ class B2(IntEnum):
755755
class B3(IntEnum):
756756
x = 1
757757

758-
# TODO: getting B1.x._value_ and B2.x._value_ to have type 'int' requires a typeshed change
759-
760758
is_x(reveal_type(B1.x.name)) # N: Revealed type is "Literal['x']"
761759
is_x(reveal_type(B1.x._name_)) # N: Revealed type is "Literal['x']"
762760
reveal_type(B1.x.value) # N: Revealed type is "builtins.int"
763-
reveal_type(B1.x._value_) # N: Revealed type is "Any"
761+
reveal_type(B1.x._value_) # N: Revealed type is "builtins.int"
764762
is_x(reveal_type(B2.x.name)) # N: Revealed type is "Literal['x']"
765763
is_x(reveal_type(B2.x._name_)) # N: Revealed type is "Literal['x']"
766764
reveal_type(B2.x.value) # N: Revealed type is "builtins.int"
@@ -2539,3 +2537,51 @@ def check(thing: Things) -> None:
25392537
return None
25402538
return None # E: Statement is unreachable
25412539
[builtins fixtures/enum.pyi]
2540+
2541+
[case testSunderValueType]
2542+
from enum import Enum, IntEnum, StrEnum, Flag, IntFlag
2543+
2544+
class Basic(Enum):
2545+
_value_: int
2546+
FOO = 1
2547+
2548+
reveal_type(Basic.FOO) # N: Revealed type is "Literal[__main__.Basic.FOO]?"
2549+
reveal_type(Basic.FOO.value) # N: Revealed type is "Literal[1]?"
2550+
reveal_type(Basic.FOO._value_) # N: Revealed type is "builtins.int"
2551+
2552+
class FromStub(Enum):
2553+
_value_: int
2554+
FOO = ...
2555+
2556+
reveal_type(FromStub.FOO) # N: Revealed type is "Literal[__main__.FromStub.FOO]?"
2557+
reveal_type(FromStub.FOO.value) # N: Revealed type is "builtins.int"
2558+
reveal_type(FromStub.FOO._value_) # N: Revealed type is "builtins.int"
2559+
2560+
class InheritedInt(IntEnum):
2561+
FOO = ...
2562+
2563+
reveal_type(InheritedInt.FOO) # N: Revealed type is "Literal[__main__.InheritedInt.FOO]?"
2564+
reveal_type(InheritedInt.FOO.value) # N: Revealed type is "builtins.int"
2565+
reveal_type(InheritedInt.FOO._value_) # N: Revealed type is "builtins.int"
2566+
2567+
class InheritedStr(StrEnum):
2568+
FOO = ...
2569+
2570+
reveal_type(InheritedStr.FOO) # N: Revealed type is "Literal[__main__.InheritedStr.FOO]?"
2571+
reveal_type(InheritedStr.FOO.value) # N: Revealed type is "builtins.str"
2572+
reveal_type(InheritedStr.FOO._value_) # N: Revealed type is "builtins.str"
2573+
2574+
class InheritedFlag(Flag):
2575+
FOO = ...
2576+
2577+
reveal_type(InheritedFlag.FOO) # N: Revealed type is "Literal[__main__.InheritedFlag.FOO]?"
2578+
reveal_type(InheritedFlag.FOO.value) # N: Revealed type is "builtins.int"
2579+
reveal_type(InheritedFlag.FOO._value_) # N: Revealed type is "builtins.int"
2580+
2581+
class InheritedIntFlag(IntFlag):
2582+
FOO = ...
2583+
2584+
reveal_type(InheritedIntFlag.FOO) # N: Revealed type is "Literal[__main__.InheritedIntFlag.FOO]?"
2585+
reveal_type(InheritedIntFlag.FOO.value) # N: Revealed type is "builtins.int"
2586+
reveal_type(InheritedIntFlag.FOO._value_) # N: Revealed type is "builtins.int"
2587+
[builtins fixtures/enum.pyi]

test-data/unit/lib-stub/enum.pyi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,16 @@ class Enum(metaclass=EnumMeta):
2929

3030
class IntEnum(int, Enum):
3131
value: int
32+
_value_: int
3233
def __new__(cls: Type[_T], value: Union[int, _T]) -> _T: ...
3334

3435
def unique(enumeration: _T) -> _T: pass
3536

3637
# In reality Flag and IntFlag are 3.6 only
3738

3839
class Flag(Enum):
40+
value: int
41+
_value_: int
3942
def __or__(self: _T, other: Union[int, _T]) -> _T: pass
4043

4144

@@ -49,6 +52,8 @@ class auto(IntFlag):
4952

5053
# It is python-3.11+ only:
5154
class StrEnum(str, Enum):
55+
_value_: str
56+
value: str
5257
def __new__(cls: Type[_T], value: str | _T) -> _T: ...
5358

5459
# It is python-3.11+ only:

0 commit comments

Comments
 (0)