From 1fed9614f25f334e8f4f008b7416081069b19cc4 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Sat, 5 Apr 2025 03:23:21 +0200 Subject: [PATCH 1/3] Check typevar defaults with `is_same_type` instead of full equality --- mypy/checker.py | 2 +- mypy/checkexpr.py | 2 +- test-data/unit/check-python312.test | 12 ++++++++ test-data/unit/check-typevar-defaults.test | 35 ++++++++++++++++++++-- 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 7d0b41c516e1..f261a88c9e97 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -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: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 099e151dd33d..9f9176986ff4 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -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) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 2f3d5e08dab3..81483ecfea6b 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -2042,3 +2042,15 @@ 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 testPEP695TypeVarDefaultAliases] +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) +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 93d20eb26f6e..226e15796edb 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -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 @@ -749,3 +747,36 @@ 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 testTypeVarDefaultAliasesTypeAliasType] +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) +[builtins fixtures/tuple.pyi] + +[case testTypeVarDefaultAliasesImplicitAlias] +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 testTypeVarDefaultAliasesExplicitAlias] +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] From c7b96701addd450e2d813fa47287fd54176e3984 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Sat, 5 Apr 2025 14:53:13 +0200 Subject: [PATCH 2/3] Add tests for namedtuple and typeddict (both were unsupported, reported in #17686 --- test-data/unit/check-python312.test | 2 +- test-data/unit/check-typevar-defaults.test | 43 ++++++++++++++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 81483ecfea6b..52025363b4cd 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -2043,7 +2043,7 @@ b: tuple[*tuple[int, ...], *tuple[int, ...]] # E: More than one Unpack in a typ [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] -[case testPEP695TypeVarDefaultAliases] +[case testPEP695TypeVarConstraintsDefaultAliases] from typing_extensions import TypeVar type K = int diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 226e15796edb..95cfa247c882 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -748,7 +748,7 @@ a: MyA = A(None, 10) reveal_type(a.a) # N: Revealed type is "Union[builtins.int, None]" [builtins fixtures/tuple.pyi] -[case testTypeVarDefaultAliasesTypeAliasType] +[case testTypeVarConstraintsDefaultAliasesTypeAliasType] from typing_extensions import TypeAliasType, TypeVar K = TypeAliasType("K", int) @@ -759,7 +759,7 @@ T2 = TypeVar("T2", str, K, default=V) T3 = TypeVar("T3", str, L, default=L) [builtins fixtures/tuple.pyi] -[case testTypeVarDefaultAliasesImplicitAlias] +[case testTypeVarConstraintsDefaultAliasesImplicitAlias] from typing_extensions import TypeVar K = int @@ -770,7 +770,7 @@ T2 = TypeVar("T2", str, K, default=V) T3 = TypeVar("T3", str, L, default=L) [builtins fixtures/tuple.pyi] -[case testTypeVarDefaultAliasesExplicitAlias] +[case testTypeVarConstraintsDefaultAliasesExplicitAlias] from typing_extensions import TypeAlias, TypeVar K: TypeAlias = int @@ -780,3 +780,40 @@ 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 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) +[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 +[builtins fixtures/tuple.pyi] From a871b876f7a372ec10829314d58512f02ae34cb9 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Fri, 18 Apr 2025 16:06:01 +0200 Subject: [PATCH 3/3] Add basic usage to testcases, add test for inline tvar defaults --- test-data/unit/check-python312.test | 12 ++++++++ test-data/unit/check-python313.test | 16 +++++++++++ test-data/unit/check-typevar-defaults.test | 33 +++++++++++++++++++++- 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 52025363b4cd..b727611573e0 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -2044,6 +2044,7 @@ b: tuple[*tuple[int, ...], *tuple[int, ...]] # E: More than one Unpack in a typ [typing fixtures/typing-full.pyi] [case testPEP695TypeVarConstraintsDefaultAliases] +from typing import Generic from typing_extensions import TypeVar type K = int @@ -2053,4 +2054,15 @@ 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] diff --git a/test-data/unit/check-python313.test b/test-data/unit/check-python313.test index 2729ad3e21d1..740f07c30ec7 100644 --- a/test-data/unit/check-python313.test +++ b/test-data/unit/check-python313.test @@ -255,3 +255,19 @@ def func_c1( [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] diff --git a/test-data/unit/check-typevar-defaults.test b/test-data/unit/check-typevar-defaults.test index 95cfa247c882..e98e0f7d12ff 100644 --- a/test-data/unit/check-typevar-defaults.test +++ b/test-data/unit/check-typevar-defaults.test @@ -749,6 +749,7 @@ 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) @@ -757,6 +758,17 @@ 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] @@ -782,7 +794,7 @@ T3 = TypeVar("T3", str, L, default=L) [builtins fixtures/tuple.pyi] [case testTypeVarConstraintsDefaultSpecialTypes] -from typing import NamedTuple +from typing import Generic, NamedTuple from typing_extensions import TypedDict, TypeVar class TD(TypedDict): @@ -793,6 +805,14 @@ class NT(NamedTuple): 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] @@ -816,4 +836,15 @@ 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]