Skip to content

Commit

Permalink
[PEP 695] Generate error if 3.12 type alias is called (#17320)
Browse files Browse the repository at this point in the history
PEP 695 type aliases raise an exception at runtime if called.

Work on #15238.
  • Loading branch information
JukkaL authored Jun 4, 2024
1 parent 93dac05 commit c762191
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 0 deletions.
4 changes: 4 additions & 0 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -4659,6 +4659,8 @@ def visit_type_application(self, tapp: TypeApplication) -> Type:
is due to slight differences in how type arguments are applied and checked.
"""
if isinstance(tapp.expr, RefExpr) and isinstance(tapp.expr.node, TypeAlias):
if tapp.expr.node.python_3_12_type_alias:
return self.named_type("typing.TypeAliasType")
# Subscription of a (generic) alias in runtime context, expand the alias.
item = instantiate_type_alias(
tapp.expr.node,
Expand Down Expand Up @@ -4721,6 +4723,8 @@ class LongName(Generic[T]): ...
x = A()
y = cast(A, ...)
"""
if alias.python_3_12_type_alias:
return self.named_type("typing.TypeAliasType")
if isinstance(alias.target, Instance) and alias.target.invalid: # type: ignore[misc]
# An invalid alias, error already has been reported
return AnyType(TypeOfAny.from_error)
Expand Down
6 changes: 6 additions & 0 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3578,6 +3578,7 @@ def f(x: B[T]) -> T: ... # without T, Any would be used here
"_is_recursive",
"eager",
"tvar_tuple_index",
"python_3_12_type_alias",
)

__match_args__ = ("name", "target", "alias_tvars", "no_args")
Expand All @@ -3593,6 +3594,7 @@ def __init__(
no_args: bool = False,
normalized: bool = False,
eager: bool = False,
python_3_12_type_alias: bool = False,
) -> None:
self._fullname = fullname
self.target = target
Expand All @@ -3605,6 +3607,7 @@ def __init__(
# it is the cached value.
self._is_recursive: bool | None = None
self.eager = eager
self.python_3_12_type_alias = python_3_12_type_alias
self.tvar_tuple_index = None
for i, t in enumerate(alias_tvars):
if isinstance(t, mypy.types.TypeVarTupleType):
Expand Down Expand Up @@ -3675,6 +3678,7 @@ def serialize(self) -> JsonDict:
"normalized": self.normalized,
"line": self.line,
"column": self.column,
"python_3_12_type_alias": self.python_3_12_type_alias,
}
return data

Expand All @@ -3692,6 +3696,7 @@ def deserialize(cls, data: JsonDict) -> TypeAlias:
normalized = data["normalized"]
line = data["line"]
column = data["column"]
python_3_12_type_alias = data["python_3_12_type_alias"]
return cls(
target,
fullname,
Expand All @@ -3700,6 +3705,7 @@ def deserialize(cls, data: JsonDict) -> TypeAlias:
alias_tvars=cast(List[mypy.types.TypeVarLikeType], alias_tvars),
no_args=no_args,
normalized=normalized,
python_3_12_type_alias=python_3_12_type_alias,
)


Expand Down
2 changes: 2 additions & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3922,6 +3922,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:
alias_tvars=alias_tvars,
no_args=no_args,
eager=eager,
python_3_12_type_alias=pep_695,
)
if isinstance(s.rvalue, (IndexExpr, CallExpr, OpExpr)) and (
not isinstance(rvalue, OpExpr)
Expand Down Expand Up @@ -5368,6 +5369,7 @@ def visit_type_alias_stmt(self, s: TypeAliasStmt) -> None:
alias_tvars=alias_tvars,
no_args=False,
eager=eager,
python_3_12_type_alias=True,
)

existing = self.current_symbol_table().get(s.name.name)
Expand Down
47 changes: 47 additions & 0 deletions test-data/unit/check-python312.test
Original file line number Diff line number Diff line change
Expand Up @@ -1313,3 +1313,50 @@ reveal_type(E[int]().mm(b'x')) # N: Revealed type is "Tuple[__main__.E[builtins
reveal_type(F[str]().m()) # N: Revealed type is "__main__.F[builtins.str]"
reveal_type(F[str]().mm(b'x')) # N: Revealed type is "Tuple[__main__.F[builtins.str], builtins.bytes]"
[builtins fixtures/tuple.pyi]

[case testPEP695CallAlias]
# mypy: enable-incomplete-feature=NewGenericSyntax

class C:
def __init__(self, x: str) -> None: ...
type A = C

class D[T]: pass
type B[T] = D[T]

reveal_type(A) # N: Revealed type is "typing.TypeAliasType"
reveal_type(B) # N: Revealed type is "typing.TypeAliasType"
reveal_type(B[int]) # N: Revealed type is "typing.TypeAliasType"

A(1) # E: "TypeAliasType" not callable
B[int]() # E: "TypeAliasType" not callable

A2 = C
B2 = D
A2(1) # E: Argument 1 to "C" has incompatible type "int"; expected "str"
B2[int]()
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]

[case testPEP695IncrementalTypeAliasKinds]
# flags: --enable-incomplete-feature=NewGenericSyntax
import a

[file a.py]
from b import A

[file a.py.2]
from b import A, B, C
A()
B()
C()

[file b.py]
from typing_extensions import TypeAlias
type A = int
B = int
C: TypeAlias = int
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]
[out2]
tmp/a.py:2: error: "TypeAliasType" not callable
7 changes: 7 additions & 0 deletions test-data/unit/check-type-aliases.test
Original file line number Diff line number Diff line change
Expand Up @@ -1075,11 +1075,15 @@ x: TestType = 42
y: TestType = 'a'
z: TestType = object() # E: Incompatible types in assignment (expression has type "object", variable has type "Union[int, str]")

reveal_type(TestType) # N: Revealed type is "typing.TypeAliasType"
TestType() # E: "TypeAliasType" not callable

class A:
ClassAlias = TypeAliasType("ClassAlias", int)
xc: A.ClassAlias = 1
yc: A.ClassAlias = "" # E: Incompatible types in assignment (expression has type "str", variable has type "int")
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]

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

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

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

[case testTypeAliasTypeNoUnpackInTypeParams311]
# flags: --python-version 3.11
Expand Down
1 change: 1 addition & 0 deletions test-data/unit/fixtures/typing-full.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ TypedDict = 0
NoReturn = 0
NewType = 0
Self = 0
Unpack = 0

T = TypeVar('T')
T_co = TypeVar('T_co', covariant=True)
Expand Down

0 comments on commit c762191

Please sign in to comment.