Skip to content

Error for assignment of functional Enum to variable of different name #16805

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

Merged
merged 2 commits into from
Apr 17, 2024
Merged
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
19 changes: 14 additions & 5 deletions mypy/semanal_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,21 @@ class A(enum.Enum):
fullname = callee.fullname
if fullname not in ENUM_BASES:
return None
items, values, ok = self.parse_enum_call_args(call, fullname.split(".")[-1])

new_class_name, items, values, ok = self.parse_enum_call_args(
call, fullname.split(".")[-1]
)
if not ok:
# Error. Construct dummy return value.
name = var_name
if is_func_scope:
name += "@" + str(call.line)
info = self.build_enum_call_typeinfo(name, [], fullname, node.line)
else:
if new_class_name != var_name:
msg = f'String argument 1 "{new_class_name}" to {fullname}(...) does not match variable name "{var_name}"'
self.fail(msg, call)

name = cast(StrExpr, call.args[0]).value
if name != var_name or is_func_scope:
# Give it a unique name derived from the line number.
Expand Down Expand Up @@ -142,7 +149,7 @@ def build_enum_call_typeinfo(

def parse_enum_call_args(
self, call: CallExpr, class_name: str
) -> tuple[list[str], list[Expression | None], bool]:
) -> tuple[str, list[str], list[Expression | None], bool]:
"""Parse arguments of an Enum call.

Return a tuple of fields, values, was there an error.
Expand Down Expand Up @@ -172,6 +179,8 @@ def parse_enum_call_args(
return self.fail_enum_call_arg(
f"{class_name}() expects a string literal as the first argument", call
)
new_class_name = value.value

items = []
values: list[Expression | None] = []
if isinstance(names, StrExpr):
Expand Down Expand Up @@ -239,13 +248,13 @@ def parse_enum_call_args(
if not values:
values = [None] * len(items)
assert len(items) == len(values)
return items, values, True
return new_class_name, items, values, True

def fail_enum_call_arg(
self, message: str, context: Context
) -> tuple[list[str], list[Expression | None], bool]:
) -> tuple[str, list[str], list[Expression | None], bool]:
self.fail(message, context)
return [], [], False
return "", [], [], False

# Helpers

Expand Down
72 changes: 28 additions & 44 deletions test-data/unit/check-enum.test
Original file line number Diff line number Diff line change
Expand Up @@ -452,55 +452,39 @@ from enum import Enum, IntEnum

PictureSize = Enum('PictureSize', 'P0 P1 P2 P3 P4 P5 P6 P7 P8', type=str, module=__name__)
fake_enum1 = Enum('fake_enum1', ['a', 'b'])
fake_enum2 = Enum('fake_enum1', names=['a', 'b'])
fake_enum3 = Enum(value='fake_enum1', names=['a', 'b'])
fake_enum4 = Enum(value='fake_enum1', names=['a', 'b'] , module=__name__)
fake_enum2 = Enum('fake_enum2', names=['a', 'b'])
fake_enum3 = Enum(value='fake_enum3', names=['a', 'b'])
fake_enum4 = Enum(value='fake_enum4', names=['a', 'b'] , module=__name__)

[case testFunctionalEnumErrors]
from enum import Enum, IntEnum
A = Enum('A')
B = Enum('B', 42)
C = Enum('C', 'a b', 'x', 'y', 'z', 'p', 'q')
D = Enum('D', foo)
A = Enum('A') # E: Too few arguments for Enum()
B = Enum('B', 42) # E: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members
C = Enum('C', 'a b', 'x', 'y', 'z', 'p', 'q') # E: Too many arguments for Enum()
D = Enum('D', foo) # E: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members \
# E: Name "foo" is not defined
bar = 'x y z'
E = Enum('E', bar)
I = IntEnum('I')
J = IntEnum('I', 42)
K = IntEnum('I', 'p q', 'x', 'y', 'z', 'p', 'q')
L = Enum('L', ' ')
M = Enum('M', ())
N = IntEnum('M', [])
P = Enum('P', [42])
Q = Enum('Q', [('a', 42, 0)])
R = IntEnum('R', [[0, 42]])
S = Enum('S', {1: 1})
T = Enum('T', keyword='a b')
U = Enum('U', *['a'])
V = Enum('U', **{'a': 1})
E = Enum('E', bar) # E: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members
I = IntEnum('I') # E: Too few arguments for IntEnum()
J = IntEnum('I', 42) # E: Second argument of IntEnum() must be string, tuple, list or dict literal for mypy to determine Enum members
K = IntEnum('I', 'p q', 'x', 'y', 'z', 'p', 'q') # E: Too many arguments for IntEnum()
L = Enum('L', ' ') # E: Enum() needs at least one item
M = Enum('M', ()) # E: Enum() needs at least one item
N = IntEnum('M', []) # E: IntEnum() needs at least one item
P = Enum('P', [42]) # E: Enum() with tuple or list expects strings or (name, value) pairs
Q = Enum('Q', [('a', 42, 0)]) # E: Enum() with tuple or list expects strings or (name, value) pairs
R = IntEnum('R', [[0, 42]]) # E: IntEnum() with tuple or list expects strings or (name, value) pairs
S = Enum('S', {1: 1}) # E: Enum() with dict literal requires string literals
T = Enum('T', keyword='a b') # E: Unexpected keyword argument "keyword"
U = Enum('U', *['a']) # E: Unexpected arguments to Enum()
V = Enum('U', **{'a': 1}) # E: Unexpected arguments to Enum()
W = Enum('W', 'a b')
W.c
W.c # E: "Type[W]" has no attribute "c"
X = Enum('Something', 'a b') # E: String argument 1 "Something" to enum.Enum(...) does not match variable name "X"
reveal_type(X.a) # N: Revealed type is "Literal[[email protected]]?"
X.asdf # E: "Type[Something@23]" has no attribute "asdf"

[typing fixtures/typing-medium.pyi]
[out]
main:2: error: Too few arguments for Enum()
main:3: error: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members
main:4: error: Too many arguments for Enum()
main:5: error: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members
main:5: error: Name "foo" is not defined
main:7: error: Second argument of Enum() must be string, tuple, list or dict literal for mypy to determine Enum members
main:8: error: Too few arguments for IntEnum()
main:9: error: Second argument of IntEnum() must be string, tuple, list or dict literal for mypy to determine Enum members
main:10: error: Too many arguments for IntEnum()
main:11: error: Enum() needs at least one item
main:12: error: Enum() needs at least one item
main:13: error: IntEnum() needs at least one item
main:14: error: Enum() with tuple or list expects strings or (name, value) pairs
main:15: error: Enum() with tuple or list expects strings or (name, value) pairs
main:16: error: IntEnum() with tuple or list expects strings or (name, value) pairs
main:17: error: Enum() with dict literal requires string literals
main:18: error: Unexpected keyword argument "keyword"
main:19: error: Unexpected arguments to Enum()
main:20: error: Unexpected arguments to Enum()
main:22: error: "Type[W]" has no attribute "c"

[case testFunctionalEnumFlag]
from enum import Flag, IntFlag
Expand Down Expand Up @@ -1117,7 +1101,7 @@ from enum import Enum

class A:
def __init__(self) -> None:
self.b = Enum("x", [("foo", "bar")]) # E: Enum type as attribute is not supported
self.b = Enum("b", [("foo", "bar")]) # E: Enum type as attribute is not supported

reveal_type(A().b) # N: Revealed type is "Any"

Expand Down