Skip to content

Commit 3d9256b

Browse files
authored
Support enum.nonmember for python3.11+ (#17376)
This PR adds support for https://docs.python.org/3.11/library/enum.html#enum.nonmember Refs #12841
1 parent 98a22c4 commit 3d9256b

File tree

4 files changed

+56
-3
lines changed

4 files changed

+56
-3
lines changed

mypy/checkmember.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,17 @@ def analyze_enum_class_attribute_access(
11431143
if name.startswith("__") and name.replace("_", "") != "":
11441144
return None
11451145

1146+
node = itype.type.get(name)
1147+
if node and node.type:
1148+
proper = get_proper_type(node.type)
1149+
# Support `A = nonmember(1)` function call and decorator.
1150+
if (
1151+
isinstance(proper, Instance)
1152+
and proper.type.fullname == "enum.nonmember"
1153+
and proper.args
1154+
):
1155+
return proper.args[0]
1156+
11461157
enum_literal = LiteralType(name, fallback=itype)
11471158
return itype.copy_modified(last_known_value=enum_literal)
11481159

mypy/plugins/enums.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,15 @@
2020
from mypy.semanal_enum import ENUM_BASES
2121
from mypy.subtypes import is_equivalent
2222
from mypy.typeops import fixup_partial_type, make_simplified_union
23-
from mypy.types import CallableType, Instance, LiteralType, ProperType, Type, get_proper_type
23+
from mypy.types import (
24+
CallableType,
25+
Instance,
26+
LiteralType,
27+
ProperType,
28+
Type,
29+
get_proper_type,
30+
is_named_instance,
31+
)
2432

2533
ENUM_NAME_ACCESS: Final = {f"{prefix}.name" for prefix in ENUM_BASES} | {
2634
f"{prefix}._name_" for prefix in ENUM_BASES
@@ -159,7 +167,7 @@ class SomeEnum:
159167

160168
stnodes = (info.get(name) for name in info.names)
161169

162-
# Enums _can_ have methods and instance attributes.
170+
# Enums _can_ have methods, instance attributes, and `nonmember`s.
163171
# Omit methods and attributes created by assigning to self.*
164172
# for our value inference.
165173
node_types = (
@@ -170,7 +178,8 @@ class SomeEnum:
170178
proper_types = [
171179
_infer_value_type_with_auto_fallback(ctx, t)
172180
for t in node_types
173-
if t is None or not isinstance(t, CallableType)
181+
if t is None
182+
or (not isinstance(t, CallableType) and not is_named_instance(t, "enum.nonmember"))
174183
]
175184
underlying_type = _first(proper_types)
176185
if underlying_type is None:

test-data/unit/check-enum.test

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2138,3 +2138,31 @@ elif e == MyEnum.B:
21382138
else:
21392139
reveal_type(e) # E: Statement is unreachable
21402140
[builtins fixtures/dict.pyi]
2141+
2142+
2143+
[case testEnumNonMemberSupport]
2144+
# flags: --python-version 3.11
2145+
# This was added in 3.11
2146+
from enum import Enum, nonmember
2147+
2148+
class My(Enum):
2149+
a = 1
2150+
b = 2
2151+
c = nonmember(3)
2152+
2153+
reveal_type(My.a) # N: Revealed type is "Literal[__main__.My.a]?"
2154+
reveal_type(My.b) # N: Revealed type is "Literal[__main__.My.b]?"
2155+
reveal_type(My.c) # N: Revealed type is "builtins.int"
2156+
2157+
def accepts_my(my: My):
2158+
reveal_type(my.value) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]"
2159+
2160+
class Other(Enum):
2161+
a = 1
2162+
@nonmember
2163+
class Support:
2164+
b = 2
2165+
2166+
reveal_type(Other.a) # N: Revealed type is "Literal[__main__.Other.a]?"
2167+
reveal_type(Other.Support.b) # N: Revealed type is "builtins.int"
2168+
[builtins fixtures/dict.pyi]

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,8 @@ class auto(IntFlag):
4848
# It is python-3.11+ only:
4949
class StrEnum(str, Enum):
5050
def __new__(cls: Type[_T], value: str | _T) -> _T: ...
51+
52+
# It is python-3.11+ only:
53+
class nonmember(Generic[_T]):
54+
value: _T
55+
def __init__(self, value: _T) -> None: ...

0 commit comments

Comments
 (0)