Skip to content

Commit

Permalink
Support enum Literals in the enum plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
A5rocks committed Jul 11, 2023
1 parent 67cc059 commit cd5c103
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 8 deletions.
24 changes: 16 additions & 8 deletions mypy/plugins/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,11 @@ class SomeEnum:
return make_simplified_union(cast(Sequence[Type], proper_types))
return ctx.default_attr_type

assert isinstance(ctx.type, Instance)
info = ctx.type.type
assert isinstance(ctx.type, (Instance, LiteralType))
if isinstance(ctx.type, Instance):
info = ctx.type.type
else:
info = ctx.type.fallback.type

# As long as mypy doesn't understand attribute creation in __new__,
# there is no way to predict the value type if the enum class has a
Expand Down Expand Up @@ -241,15 +244,20 @@ def _extract_underlying_field_name(typ: Type) -> str | None:
We can examine this Literal fallback to retrieve the string.
"""
typ = get_proper_type(typ)
if not isinstance(typ, Instance):
if not isinstance(typ, (Instance, LiteralType)):
return None

if not typ.type.is_enum:
return None
if isinstance(typ, Instance):
if not typ.type.is_enum:
return None

underlying_literal = typ.last_known_value
if underlying_literal is None:
return None
underlying_literal = typ.last_known_value
if underlying_literal is None:
return None
else:
underlying_literal = typ
if not typ.is_enum_literal():
return None

# The checks above have verified this LiteralType is representing an enum value,
# which means the 'value' field is guaranteed to be the name of the enum field
Expand Down
29 changes: 29 additions & 0 deletions test-data/unit/check-enum.test
Original file line number Diff line number Diff line change
Expand Up @@ -2125,3 +2125,32 @@ class AllPartialList(Enum):

def check(self) -> None:
reveal_type(self.value) # N: Revealed type is "builtins.list[Any]"

[case testEnumLiteralsSupportedByPlugin]
from enum import Enum
from typing_extensions import Literal

class E(Enum):
A = 1
B = 2

x: Literal[E.A]
reveal_type(x) # N: Revealed type is "Literal[__main__.E.A]"
reveal_type(x.value) # N: Revealed type is "Literal[1]?"
reveal_type(x.name) # N: Revealed type is "Literal['A']?"

e: E
if e is E.A:
reveal_type(e) # N: Revealed type is "Literal[__main__.E.A]"
reveal_type(e.value) # N: Revealed type is "Literal[1]?"
reveal_type(e.name) # N: Revealed type is "Literal['A']?"
else:
reveal_type(e) # N: Revealed type is "Literal[__main__.E.B]"
reveal_type(e.value) # N: Revealed type is "Literal[2]?"
reveal_type(e.name) # N: Revealed type is "Literal['B']?"

y: Literal[E.A, E.B]
reveal_type(y) # N: Revealed type is "Union[Literal[__main__.E.A], Literal[__main__.E.B]]"
reveal_type(y.value) # N: Revealed type is "Any"
reveal_type(y.name) # N: Revealed type is "builtins.str"
[builtins fixtures/tuple.pyi]

0 comments on commit cd5c103

Please sign in to comment.