Skip to content

Fix enum value inference with user-defined data type mixin #16320

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions mypy/plugins/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,14 @@ def _infer_value_type_with_auto_fallback(

def _implements_new(info: TypeInfo) -> bool:
"""Check whether __new__ comes from enum.Enum or was implemented in a
subclass. In the latter case, we must infer Any as long as mypy can't infer
subclass of enum.Enum. In the latter case, we must infer Any as long as mypy can't infer
the type of _value_ from assignments in __new__.

If, however, __new__ comes from a user-defined class that is not an Enum subclass (i.e.
the data type) this is allowed, because we should in general infer that an enum entry's
value has that type.
"""
type_with_new = _first(
ti
for ti in info.mro
if ti.names.get("__new__") and not ti.fullname.startswith("builtins.")
)
type_with_new = _first(ti for ti in info.mro if ti.is_enum and ti.names.get("__new__"))
if type_with_new is None:
return False
return type_with_new.fullname not in ("enum.Enum", "enum.IntEnum", "enum.StrEnum")
Expand Down
14 changes: 14 additions & 0 deletions test-data/unit/check-enum.test
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,20 @@ reveal_type(a._value_) # N: Revealed type is "Any"
[builtins fixtures/primitives.pyi]
[typing fixtures/typing-medium.pyi]

[case testValueTypeWithUserDataType]
from enum import Enum
from typing import Any

class Data:
def __new__(cls, value: Any) -> Data: pass

class DataEnum(Data, Enum):
A = Data(1)

reveal_type(DataEnum.A) # N: Revealed type is "Literal[__main__.DataEnum.A]?"
reveal_type(DataEnum.A.value) # N: Revealed type is "__main__.Data"
reveal_type(DataEnum.A._value_) # N: Revealed type is "__main__.Data"

[case testEnumNarrowedToTwoLiterals]
# Regression test: two literals of an enum would be joined
# as the full type, regardless of the amount of elements
Expand Down