Skip to content

Commit c762191

Browse files
authored
[PEP 695] Generate error if 3.12 type alias is called (#17320)
PEP 695 type aliases raise an exception at runtime if called. Work on #15238.
1 parent 93dac05 commit c762191

File tree

6 files changed

+67
-0
lines changed

6 files changed

+67
-0
lines changed

mypy/checkexpr.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4659,6 +4659,8 @@ def visit_type_application(self, tapp: TypeApplication) -> Type:
46594659
is due to slight differences in how type arguments are applied and checked.
46604660
"""
46614661
if isinstance(tapp.expr, RefExpr) and isinstance(tapp.expr.node, TypeAlias):
4662+
if tapp.expr.node.python_3_12_type_alias:
4663+
return self.named_type("typing.TypeAliasType")
46624664
# Subscription of a (generic) alias in runtime context, expand the alias.
46634665
item = instantiate_type_alias(
46644666
tapp.expr.node,
@@ -4721,6 +4723,8 @@ class LongName(Generic[T]): ...
47214723
x = A()
47224724
y = cast(A, ...)
47234725
"""
4726+
if alias.python_3_12_type_alias:
4727+
return self.named_type("typing.TypeAliasType")
47244728
if isinstance(alias.target, Instance) and alias.target.invalid: # type: ignore[misc]
47254729
# An invalid alias, error already has been reported
47264730
return AnyType(TypeOfAny.from_error)

mypy/nodes.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3578,6 +3578,7 @@ def f(x: B[T]) -> T: ... # without T, Any would be used here
35783578
"_is_recursive",
35793579
"eager",
35803580
"tvar_tuple_index",
3581+
"python_3_12_type_alias",
35813582
)
35823583

35833584
__match_args__ = ("name", "target", "alias_tvars", "no_args")
@@ -3593,6 +3594,7 @@ def __init__(
35933594
no_args: bool = False,
35943595
normalized: bool = False,
35953596
eager: bool = False,
3597+
python_3_12_type_alias: bool = False,
35963598
) -> None:
35973599
self._fullname = fullname
35983600
self.target = target
@@ -3605,6 +3607,7 @@ def __init__(
36053607
# it is the cached value.
36063608
self._is_recursive: bool | None = None
36073609
self.eager = eager
3610+
self.python_3_12_type_alias = python_3_12_type_alias
36083611
self.tvar_tuple_index = None
36093612
for i, t in enumerate(alias_tvars):
36103613
if isinstance(t, mypy.types.TypeVarTupleType):
@@ -3675,6 +3678,7 @@ def serialize(self) -> JsonDict:
36753678
"normalized": self.normalized,
36763679
"line": self.line,
36773680
"column": self.column,
3681+
"python_3_12_type_alias": self.python_3_12_type_alias,
36783682
}
36793683
return data
36803684

@@ -3692,6 +3696,7 @@ def deserialize(cls, data: JsonDict) -> TypeAlias:
36923696
normalized = data["normalized"]
36933697
line = data["line"]
36943698
column = data["column"]
3699+
python_3_12_type_alias = data["python_3_12_type_alias"]
36953700
return cls(
36963701
target,
36973702
fullname,
@@ -3700,6 +3705,7 @@ def deserialize(cls, data: JsonDict) -> TypeAlias:
37003705
alias_tvars=cast(List[mypy.types.TypeVarLikeType], alias_tvars),
37013706
no_args=no_args,
37023707
normalized=normalized,
3708+
python_3_12_type_alias=python_3_12_type_alias,
37033709
)
37043710

37053711

mypy/semanal.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3922,6 +3922,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:
39223922
alias_tvars=alias_tvars,
39233923
no_args=no_args,
39243924
eager=eager,
3925+
python_3_12_type_alias=pep_695,
39253926
)
39263927
if isinstance(s.rvalue, (IndexExpr, CallExpr, OpExpr)) and (
39273928
not isinstance(rvalue, OpExpr)
@@ -5368,6 +5369,7 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None:
53685369
alias_tvars=alias_tvars,
53695370
no_args=False,
53705371
eager=eager,
5372+
python_3_12_type_alias=True,
53715373
)
53725374

53735375
existing = self.current_symbol_table().get(s.name.name)

test-data/unit/check-python312.test

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,3 +1313,50 @@ reveal_type(E[int]().mm(b'x')) # N: Revealed type is "Tuple[__main__.E[builtins
13131313
reveal_type(F[str]().m()) # N: Revealed type is "__main__.F[builtins.str]"
13141314
reveal_type(F[str]().mm(b'x')) # N: Revealed type is "Tuple[__main__.F[builtins.str], builtins.bytes]"
13151315
[builtins fixtures/tuple.pyi]
1316+
1317+
[case testPEP695CallAlias]
1318+
# mypy: enable-incomplete-feature=NewGenericSyntax
1319+
1320+
class C:
1321+
def __init__(self, x: str) -> None: ...
1322+
type A = C
1323+
1324+
class D[T]: pass
1325+
type B[T] = D[T]
1326+
1327+
reveal_type(A) # N: Revealed type is "typing.TypeAliasType"
1328+
reveal_type(B) # N: Revealed type is "typing.TypeAliasType"
1329+
reveal_type(B[int]) # N: Revealed type is "typing.TypeAliasType"
1330+
1331+
A(1) # E: "TypeAliasType" not callable
1332+
B[int]() # E: "TypeAliasType" not callable
1333+
1334+
A2 = C
1335+
B2 = D
1336+
A2(1) # E: Argument 1 to "C" has incompatible type "int"; expected "str"
1337+
B2[int]()
1338+
[builtins fixtures/tuple.pyi]
1339+
[typing fixtures/typing-full.pyi]
1340+
1341+
[case testPEP695IncrementalTypeAliasKinds]
1342+
# flags: --enable-incomplete-feature=NewGenericSyntax
1343+
import a
1344+
1345+
[file a.py]
1346+
from b import A
1347+
1348+
[file a.py.2]
1349+
from b import A, B, C
1350+
A()
1351+
B()
1352+
C()
1353+
1354+
[file b.py]
1355+
from typing_extensions import TypeAlias
1356+
type A = int
1357+
B = int
1358+
C: TypeAlias = int
1359+
[builtins fixtures/tuple.pyi]
1360+
[typing fixtures/typing-full.pyi]
1361+
[out2]
1362+
tmp/a.py:2: error: "TypeAliasType" not callable

test-data/unit/check-type-aliases.test

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,11 +1075,15 @@ x: TestType = 42
10751075
y: TestType = 'a'
10761076
z: TestType = object() # E: Incompatible types in assignment (expression has type "object", variable has type "Union[int, str]")
10771077

1078+
reveal_type(TestType) # N: Revealed type is "typing.TypeAliasType"
1079+
TestType() # E: "TypeAliasType" not callable
1080+
10781081
class A:
10791082
ClassAlias = TypeAliasType("ClassAlias", int)
10801083
xc: A.ClassAlias = 1
10811084
yc: A.ClassAlias = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int")
10821085
[builtins fixtures/tuple.pyi]
1086+
[typing fixtures/typing-full.pyi]
10831087

10841088
[case testTypeAliasTypeInvalid]
10851089
from typing_extensions import TypeAliasType
@@ -1094,6 +1098,7 @@ T3 = TypeAliasType("T3", -1) # E: Invalid type: try using Literal[-1] instead?
10941098
t3: T3
10951099
reveal_type(t3) # N: Revealed type is "Any"
10961100
[builtins fixtures/tuple.pyi]
1101+
[typing fixtures/typing-full.pyi]
10971102

10981103
[case testTypeAliasTypeGeneric]
10991104
from typing import Callable, Dict, Generic, TypeVar, Tuple
@@ -1140,6 +1145,7 @@ ParamAlias2 = TypeAliasType("ParamAlias2", G[P, T], type_params=(P, T))
11401145
xp: ParamAlias2[[int], str]
11411146
reveal_type(xp) # N: Revealed type is "__main__.G[[builtins.int], builtins.str]"
11421147
[builtins fixtures/dict.pyi]
1148+
[typing fixtures/typing-full.pyi]
11431149

11441150
[case testTypeAliasTypeInvalidGeneric]
11451151
from typing_extensions import TypeAliasType, TypeVarTuple, ParamSpec
@@ -1200,6 +1206,7 @@ class A(Generic[T]):
12001206
x: A.Ta11 = {"a": 1}
12011207
reveal_type(x) # N: Revealed type is "builtins.dict[builtins.str, Any]"
12021208
[builtins fixtures/dict.pyi]
1209+
[typing fixtures/typing-full.pyi]
12031210

12041211
[case testTypeAliasTypeNoUnpackInTypeParams311]
12051212
# flags: --python-version 3.11

test-data/unit/fixtures/typing-full.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ TypedDict = 0
3535
NoReturn = 0
3636
NewType = 0
3737
Self = 0
38+
Unpack = 0
3839

3940
T = TypeVar('T')
4041
T_co = TypeVar('T_co', covariant=True)

0 commit comments

Comments
 (0)