Skip to content
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

Fix enum .value with tuple #15637

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
11 changes: 10 additions & 1 deletion mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -2468,6 +2468,10 @@ def format_literal_value(typ: LiteralType) -> str:
if itype.type.fullname == "typing._SpecialForm":
# This is not a real type but used for some typing-related constructs.
return "<typing special form>"
if itype.last_known_value and (
fullnames and f"typing.Literal[{itype.last_known_value.value}]" in fullnames
):
return format(itype.last_known_value)
if itype.type.fullname in reverse_builtin_aliases and not options.use_lowercase_names():
alias = reverse_builtin_aliases[itype.type.fullname]
base_str = alias.split(".")[-1]
Expand Down Expand Up @@ -2652,7 +2656,12 @@ def find_type_overlaps(*types: Type) -> set[str]:
d: dict[str, set[str]] = {}
A5rocks marked this conversation as resolved.
Show resolved Hide resolved
for type in types:
for inst in collect_all_instances(type):
d.setdefault(inst.type.name, set()).add(inst.type.fullname)
if inst.last_known_value:
d.setdefault(inst.type.name, set()).add(
f"typing.Literal[{inst.last_known_value.value}]"
)
else:
d.setdefault(inst.type.name, set()).add(inst.type.fullname)
for shortname in d.keys():
if f"typing.{shortname}" in TYPES_FOR_UNIMPORTED_HINTS:
d[shortname].add(f"typing.{shortname}")
Expand Down
5 changes: 4 additions & 1 deletion mypy/plugins/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,10 @@ def int_neg_callback(ctx: MethodContext) -> Type:
else:
return ctx.type.copy_modified(
last_known_value=LiteralType(
value=-value, fallback=ctx.type, line=ctx.type.line, column=ctx.type.column
value=-value,
fallback=ctx.type.copy_modified(last_known_value=None),
line=ctx.type.line,
column=ctx.type.column,
)
)
elif isinstance(ctx.type, LiteralType):
Expand Down
2 changes: 2 additions & 0 deletions mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,8 @@ def visit_instance(self, left: Instance) -> bool:
# and we can't have circular promotions.
if left.type.alt_promote and left.type.alt_promote.type is right.type:
return True
if left.last_known_value and right.last_known_value:
return self._is_subtype(left.last_known_value, right.last_known_value)
rname = right.type.fullname
# Always try a nominal check if possible,
# there might be errors that a user wants to silence *once*.
Expand Down
28 changes: 23 additions & 5 deletions test-data/unit/check-enum.test
Original file line number Diff line number Diff line change
Expand Up @@ -1427,7 +1427,8 @@ class Medal(Enum):
silver = 2

# Another value:
Medal.gold = 0 # E: Cannot assign to final attribute "gold"
Medal.gold = 0 # E: Cannot assign to final attribute "gold" \
# E: Incompatible types in assignment (expression has type "Literal[0]", variable has type "Literal[1]")
# Same value:
Medal.silver = 2 # E: Cannot assign to final attribute "silver"

Expand All @@ -1453,10 +1454,13 @@ reveal_type(Foo.A.value) # N: Revealed type is "Literal[1]?"

class Bar(Enum):
A = 1
B = A = 2 # E: Attempted to reuse member name "A" in Enum definition "Bar"
B = A = 2 # E: Attempted to reuse member name "A" in Enum definition "Bar" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
class Baz(Enum):
A = 1
B, A = (1, 2) # E: Attempted to reuse member name "A" in Enum definition "Baz"
B, A = (1, 2) # E: Attempted to reuse member name "A" in Enum definition "Baz" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")

[builtins fixtures/tuple.pyi]

[case testEnumReusedKeysOverlapWithLocalVar]
Expand Down Expand Up @@ -1764,8 +1768,10 @@ class EI(IntEnum):
__annotations__ = {'a': int}
__dict__ = {'a': 1}

E._order_ = 'a' # E: Cannot assign to final attribute "_order_"
EI.value = 2 # E: Cannot assign to final attribute "value"
E._order_ = 'a' # E: Cannot assign to final attribute "_order_" \
# E: Incompatible types in assignment (expression has type "Literal['a']", variable has type "Literal['X Y']")
EI.value = 2 # E: Cannot assign to final attribute "value" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
[builtins fixtures/dict.pyi]

[case testEnumNotFinalWithMethodsAndUninitializedValues]
Expand Down Expand Up @@ -2125,3 +2131,15 @@ class AllPartialList(Enum):

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

[case testEnumValueWithTupleType]
import enum
from typing import Tuple

class Color(enum.Enum):
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

x: Color
reveal_type(x.value) # N: Revealed type is "Any"
[builtins fixtures/tuple.pyi]
115 changes: 77 additions & 38 deletions test-data/unit/check-final.test
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,14 @@ def f2() -> None:
y = 1
y
y: Final = 2 # E: Cannot redefine an existing name as final
y = 3 # E: Cannot assign to final name "y"
y = 3 # E: Cannot assign to final name "y" \
# E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Literal[2]")

z: Final = 1
z: Final = 2 # E: Cannot redefine an existing name as final
z = 3 # E: Cannot assign to final name "z"
z: Final = 2 # E: Cannot redefine an existing name as final \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
z = 3 # E: Cannot assign to final name "z" \
# E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Literal[1]")

[case testFinalReassignModuleVar2]
# flags: --allow-redefinition
Expand All @@ -379,7 +382,8 @@ x: Final = 1
x
def f() -> int:
global x
x = 3 # E: Cannot assign to final name "x"
x = 3 # E: Cannot assign to final name "x" \
# E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Literal[1]")
return x

y = 1
Expand All @@ -395,10 +399,12 @@ from typing import Final

x: Final = 1
x
x = 2 # E: Cannot assign to final name "x"
x = 2 # E: Cannot assign to final name "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
def f() -> int:
global x
x = 3 # E: Cannot assign to final name "x"
x = 3 # E: Cannot assign to final name "x" \
# E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Literal[1]")
return x

x2: Final = 1
Expand All @@ -413,8 +419,10 @@ y: Final = 2 # E: Cannot redefine an existing name as final
y = 3 # E: Cannot assign to final name "y"

z: Final = 1
z: Final = 2 # E: Cannot redefine an existing name as final
z = 3 # E: Cannot assign to final name "z"
z: Final = 2 # E: Cannot redefine an existing name as final \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
z = 3 # E: Cannot assign to final name "z" \
# E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Literal[1]")

[case testFinalReassignModuleReexport]

Expand Down Expand Up @@ -453,10 +461,12 @@ def f() -> None:
x = 1 # E: Cannot assign to final name "x"

y: Final = 1
y: Final = 2 # E: Cannot redefine an existing name as final
y: Final = 2 # E: Cannot redefine an existing name as final \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
def nested() -> None:
nonlocal nl
nl = 1 # E: Cannot assign to final name "nl"
nl = 1 # E: Cannot assign to final name "nl" \
# E: Incompatible types in assignment (expression has type "Literal[1]", variable has type "Literal[0]")
[out]

[case testFinalReassignModuleVarExternal]
Expand All @@ -472,7 +482,8 @@ from typing import Final

class C:
x: Final = 1
x = 2 # E: Cannot assign to final name "x"
x = 2 # E: Cannot assign to final name "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")

y = 1 # E: Cannot assign to final name "y"
y: Final = 2 # E: Cannot redefine an existing name as final
Expand All @@ -487,7 +498,8 @@ class C:
self.y = 1
self.y: Final = 2 # E: Cannot redefine an existing name as final
def meth(self) -> None:
self.x = 2 # E: Cannot assign to final attribute "x"
self.x = 2 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
[out]

[case testFinalReassignInstanceVarClassVsInit]
Expand All @@ -499,7 +511,8 @@ class C:
def __init__(self) -> None:
# Methods are processed after top-level in new analyzer.
self.x: Final = 1 # E: Cannot redefine an existing name as final
self.y = 2 # E: Cannot assign to final attribute "y"
self.y = 2 # E: Cannot assign to final attribute "y" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
x = 2
[out]

Expand All @@ -511,14 +524,19 @@ class C:
def __init__(self) -> None:
self.y: Final = 1
def meth(self) -> None:
self.x = 2 # E: Cannot assign to final attribute "x"
self.y = 2 # E: Cannot assign to final attribute "y"
self.x = 2 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
self.y = 2 # E: Cannot assign to final attribute "y" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
def other(self) -> None:
self.x = 2 # E: Cannot assign to final attribute "x"
self.y = 2 # E: Cannot assign to final attribute "y"
self.x = 2 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
self.y = 2 # E: Cannot assign to final attribute "y" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
@classmethod
def cm(cls) -> None:
cls.x = 2 # E: Cannot assign to final attribute "x"
cls.x = 2 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
cls.y # E: Cannot access final instance attribute "y" on class object
[builtins fixtures/classmethod.pyi]
[out]
Expand All @@ -533,10 +551,13 @@ class C:

class D(C): pass

C.x = 2 # E: Cannot assign to final attribute "x"
D.x = 2 # E: Cannot assign to final attribute "x"
C.x = 2 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
D.x = 2 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
D.y = 2 # E: Cannot access final instance attribute "y" on class object \
# E: Cannot assign to final attribute "y"
# E: Cannot assign to final attribute "y" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
[out]

[case testFinalReassignInstanceVarExternalInstance]
Expand All @@ -549,9 +570,12 @@ class C:

class D(C): pass

C().x = 2 # E: Cannot assign to final attribute "x"
D().x = 2 # E: Cannot assign to final attribute "x"
D().y = 2 # E: Cannot assign to final attribute "y"
C().x = 2 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
D().x = 2 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
D().y = 2 # E: Cannot assign to final attribute "y" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
[out]

[case testFinalWorksWithComplexTargets]
Expand Down Expand Up @@ -629,8 +653,10 @@ class B(A):
def __init__(self) -> None:
self.y: Final = 1
class C(B):
x: Final = 2 # E: Cannot override final attribute "x" (previously declared in base class "B")
y: Final = 2 # E: Cannot override final attribute "y" (previously declared in base class "B")
x: Final = 2 # E: Cannot override final attribute "x" (previously declared in base class "B") \
# E: Incompatible types in assignment (expression has type "Literal[2]", base class "B" defined the type as "Literal[1]")
y: Final = 2 # E: Cannot override final attribute "y" (previously declared in base class "B") \
# E: Incompatible types in assignment (expression has type "Literal[2]", base class "B" defined the type as "Literal[1]")
[builtins fixtures/property.pyi]
[out]

Expand All @@ -648,11 +674,15 @@ class B(A):
self.y: Final = 1
class C(B):
def __init__(self) -> None:
self.x = 2 # E: Cannot assign to final attribute "x"
self.y = 2 # E: Cannot assign to final attribute "y"
self.x = 2 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
self.y = 2 # E: Cannot assign to final attribute "y" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")
def meth(self) -> None:
self.x = 3 # E: Cannot assign to final attribute "x"
self.y = 3 # E: Cannot assign to final attribute "y"
self.x = 3 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Literal[1]")
self.y = 3 # E: Cannot assign to final attribute "y" \
# E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Literal[1]")
[builtins fixtures/property.pyi]
[out]

Expand All @@ -670,8 +700,10 @@ class B(A):
self.y: Final = 1
class C(B):
def __init__(self) -> None:
self.x: Final = 2 # E: Cannot override final attribute "x" (previously declared in base class "B")
self.y: Final = 2 # E: Cannot override final attribute "y" (previously declared in base class "B")
self.x: Final = 2 # E: Cannot override final attribute "x" (previously declared in base class "B") \
# E: Incompatible types in assignment (expression has type "Literal[2]", base class "B" defined the type as "Literal[1]")
self.y: Final = 2 # E: Cannot override final attribute "y" (previously declared in base class "B") \
# E: Incompatible types in assignment (expression has type "Literal[2]", base class "B" defined the type as "Literal[1]")
[builtins fixtures/property.pyi]
[out]

Expand Down Expand Up @@ -741,9 +773,12 @@ class B:
self.x = 2
class C(A, B): ... # E: Cannot override writable attribute "x" with a final one
class D(B, A): ... # E: Cannot override final attribute "x" (previously declared in base class "A")
C.x = 3 # E: Cannot assign to final attribute "x"
C.x = 3 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[3]", variable has type "Literal[1]")
D.x = 3 # E: Cannot assign to final attribute "x"
C().x = 4 # E: Cannot assign to final attribute "x"
C().x = 4 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[4]", variable has type "Literal[1]")
# TODO: this missing error is a sign of a bug!!
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also wrote up #15640 while doing this (obviously).

I still need to make a minimized issue for this TODO.

D().x = 4 # E: Cannot assign to final attribute "x"
[out]

Expand Down Expand Up @@ -1018,11 +1053,13 @@ class D(B):
from typing_extensions import final, Final

x: Final = 1
x = 2 # E: Cannot assign to final name "x"
x = 2 # E: Cannot assign to final name "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")

class S:
x: Final = 1
S.x = 2 # E: Cannot assign to final attribute "x"
S.x = 2 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")

class B:
@final
Expand All @@ -1040,11 +1077,13 @@ class E(F): ... # E: Cannot inherit from final class "F"
from typing_extensions import final as f, Final as F

x: F = 1
x = 2 # E: Cannot assign to final name "x"
x = 2 # E: Cannot assign to final name "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")

class S:
x: F = 1
S.x = 2 # E: Cannot assign to final attribute "x"
S.x = 2 # E: Cannot assign to final attribute "x" \
# E: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]")

class B:
@f
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-generic-alias.test
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,11 @@ t10: Tuple[int, ...] = t09
A = tuple[int, ...]
a: A = ()
b: A = (1, 2, 3)
c: A = ('x', 'y') # E: Incompatible types in assignment (expression has type "Tuple[str, str]", variable has type "Tuple[int, ...]")
c: A = ('x', 'y') # E: Incompatible types in assignment (expression has type "Tuple[Literal['x'], Literal['y']]", variable has type "Tuple[int, ...]")

B = tuple[int, str]
x: B = (1, 'x')
y: B = ('x', 1) # E: Incompatible types in assignment (expression has type "Tuple[str, int]", variable has type "Tuple[int, str]")
y: B = ('x', 1) # E: Incompatible types in assignment (expression has type "Tuple[Literal['x'], Literal[1]]", variable has type "Tuple[builtins.int, builtins.str]")

reveal_type(tuple[int, ...]()) # N: Revealed type is "builtins.tuple[builtins.int, ...]"
[builtins fixtures/tuple.pyi]
Expand Down
5 changes: 3 additions & 2 deletions test-data/unit/check-generics.test
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,7 @@ def f(x: T) -> UNode[T]:
reveal_type(f(1)) # N: Revealed type is "Union[builtins.int, __main__.Node[builtins.int]]"

TNode = Union[T, Node[int]]
s = 1 # type: TNode[str] # E: Incompatible types in assignment (expression has type "int", variable has type "Union[str, Node[int]]")
s = 1 # type: TNode[str] # E: Incompatible types in assignment (expression has type "Literal[1]", variable has type "Union[str, Node[builtins.int]]")

if not isinstance(s, str):
s.x = 1
Expand All @@ -815,6 +815,7 @@ z = None # type: TNode # Same as TNode[Any]
z.x
z.foo() # E: Item "Node[int]" of "Union[Any, Node[int]]" has no attribute "foo"


[builtins fixtures/isinstance.pyi]

[case testGenericTypeAliasesTuple]
Expand All @@ -834,7 +835,7 @@ reveal_type(x) # N: Revealed type is "builtins.int"
def f2(x: IntTP[T]) -> IntTP[T]:
return x

f2((1, 2, 3)) # E: Argument 1 to "f2" has incompatible type "Tuple[int, int, int]"; expected "Tuple[int, <nothing>]"
f2((1, 2, 3)) # E: Argument 1 to "f2" has incompatible type "Tuple[Literal[1], Literal[2], Literal[3]]"; expected "Tuple[builtins.int, <nothing>]"
reveal_type(f2((1, 'x'))) # N: Revealed type is "Tuple[builtins.int, builtins.str]"

[builtins fixtures/for.pyi]
Expand Down
Loading