Skip to content

Support type aliases, NamedTuple and TypedDict in constrained TypeVar defaults #2

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 4 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
2 changes: 1 addition & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2658,7 +2658,7 @@ def check_typevar_defaults(self, tvars: Sequence[TypeVarLikeType]) -> None:
continue
if not is_subtype(tv.default, tv.upper_bound):
self.fail("TypeVar default must be a subtype of the bound type", tv)
if tv.values and not any(tv.default == value for value in tv.values):
if tv.values and not any(is_same_type(tv.default, value) for value in tv.values):
self.fail("TypeVar default must be one of the constraint types", tv)

def check_enum(self, defn: ClassDef) -> None:
Expand Down
2 changes: 1 addition & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -6210,7 +6210,7 @@ def visit_type_var_expr(self, e: TypeVarExpr) -> Type:
):
if not is_subtype(p_default, e.upper_bound):
self.chk.fail("TypeVar default must be a subtype of the bound type", e)
if e.values and not any(p_default == value for value in e.values):
if e.values and not any(is_same_type(p_default, value) for value in e.values):
self.chk.fail("TypeVar default must be one of the constraint types", e)
return AnyType(TypeOfAny.special_form)

Expand Down
24 changes: 24 additions & 0 deletions test-data/unit/check-python312.test
Original file line number Diff line number Diff line change
Expand Up @@ -2042,3 +2042,27 @@ tuple[*tuple[int, ...], *tuple[int, ...]] # E: More than one Unpack in a type i
b: tuple[*tuple[int, ...], *tuple[int, ...]] # E: More than one Unpack in a type is not allowed
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]

[case testPEP695TypeVarConstraintsDefaultAliases]
from typing import Generic
from typing_extensions import TypeVar

type K = int
type V = int
type L = list[int]

T1 = TypeVar("T1", str, K, default=K)
T2 = TypeVar("T2", str, K, default=V)
T3 = TypeVar("T3", str, L, default=L)

class A1(Generic[T1]):
x: T1
class A2(Generic[T2]):
x: T2
class A3(Generic[T3]):
x: T3

reveal_type(A1().x) # N: Revealed type is "builtins.int"
reveal_type(A2().x) # N: Revealed type is "builtins.int"
reveal_type(A3().x) # N: Revealed type is "builtins.list[builtins.int]"
[builtins fixtures/tuple.pyi]
16 changes: 16 additions & 0 deletions test-data/unit/check-python313.test
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,19 @@ def func_d1(
reveal_type(d) # N: Revealed type is "__main__.A[builtins.float, builtins.str]"
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]

[case testTypeVarConstraintsDefaultAliasesInline]
type K = int
type V = int

class A1[T: (str, int) = K]:
x: T
class A2[T: (str, K) = K]:
x: T
class A3[T: (str, K) = V]:
x: T

reveal_type(A1().x) # N: Revealed type is "builtins.int"
reveal_type(A2().x) # N: Revealed type is "builtins.int"
reveal_type(A3().x) # N: Revealed type is "builtins.int"
[builtins fixtures/tuple.pyi]
103 changes: 101 additions & 2 deletions test-data/unit/check-typevar-defaults.test
Original file line number Diff line number Diff line change
Expand Up @@ -729,8 +729,6 @@ class C(Generic[_I]): pass
t: type[C] | int = C
[builtins fixtures/tuple.pyi]



[case testGenericTypeAliasWithDefaultTypeVarPreservesNoneInDefault]
from typing_extensions import TypeVar
from typing import Generic, Union
Expand All @@ -749,3 +747,104 @@ MyA = A[T1, int]
a: MyA = A(None, 10)
reveal_type(a.a) # N: Revealed type is "Union[builtins.int, None]"
[builtins fixtures/tuple.pyi]

[case testTypeVarConstraintsDefaultAliasesTypeAliasType]
from typing import Generic
from typing_extensions import TypeAliasType, TypeVar

K = TypeAliasType("K", int)
V = TypeAliasType("V", int)
L = TypeAliasType("L", list[int])
T1 = TypeVar("T1", str, K, default=K)
T2 = TypeVar("T2", str, K, default=V)
T3 = TypeVar("T3", str, L, default=L)

class A1(Generic[T1]):
x: T1
class A2(Generic[T2]):
x: T2
class A3(Generic[T3]):
x: T3

reveal_type(A1().x) # N: Revealed type is "builtins.int"
reveal_type(A2().x) # N: Revealed type is "builtins.int"
reveal_type(A3().x) # N: Revealed type is "builtins.list[builtins.int]"
[builtins fixtures/tuple.pyi]

[case testTypeVarConstraintsDefaultAliasesImplicitAlias]
from typing_extensions import TypeVar

K = int
V = int
L = list[int]
T1 = TypeVar("T1", str, K, default=K)
T2 = TypeVar("T2", str, K, default=V)
T3 = TypeVar("T3", str, L, default=L)
[builtins fixtures/tuple.pyi]

[case testTypeVarConstraintsDefaultAliasesExplicitAlias]
from typing_extensions import TypeAlias, TypeVar

K: TypeAlias = int
V: TypeAlias = int
L: TypeAlias = list[int]
T1 = TypeVar("T1", str, K, default=K)
T2 = TypeVar("T2", str, K, default=V)
T3 = TypeVar("T3", str, L, default=L)
[builtins fixtures/tuple.pyi]

[case testTypeVarConstraintsDefaultSpecialTypes]
from typing import Generic, NamedTuple
from typing_extensions import TypedDict, TypeVar

class TD(TypedDict):
foo: str

class NT(NamedTuple):
foo: str

T1 = TypeVar("T1", str, TD, default=TD)
T2 = TypeVar("T2", str, NT, default=NT)

class A1(Generic[T1]):
x: T1
class A2(Generic[T2]):
x: T2

reveal_type(A1().x) # N: Revealed type is "TypedDict('__main__.TD', {'foo': builtins.str})"
reveal_type(A2().x) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.NT]"
[builtins fixtures/tuple.pyi]

[case testTypeVarConstraintsDefaultSpecialTypesGeneric]
from typing import Generic, NamedTuple
from typing_extensions import TypedDict, TypeVar

T = TypeVar("T")

class TD(TypedDict, Generic[T]):
foo: T
class TD2(TD[int]): pass
class TD3(TD[int]):
bar: str

class NT(NamedTuple, Generic[T]):
foo: T
class NT2(NT[int]): pass

T1 = TypeVar("T1", str, TD[int], default=TD[int])
T2 = TypeVar("T2", str, NT[int], default=NT[int])
T3 = TypeVar("T3", str, TD2, default=TD[int])
T4 = TypeVar("T4", str, TD3, default=TD[int]) # E: TypeVar default must be one of the constraint types
T5 = TypeVar("T5", str, NT2, default=NT[int]) # E: TypeVar default must be one of the constraint types

class A1(Generic[T1]):
x: T1
class A2(Generic[T2]):
x: T2
class A3(Generic[T3]):
x: T3

reveal_type(A1().x) # N: Revealed type is "TypedDict('__main__.TD', {'foo': builtins.int})"
reveal_type(A2().x) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.NT[builtins.int]]"
reveal_type(A3().x) # N: Revealed type is "TypedDict('__main__.TD', {'foo': builtins.int})"
[builtins fixtures/tuple.pyi]