-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
[PEP 695] Partial support for new type parameter syntax in Python 3.12 #17233
Changes from 35 commits
725a224
3058b84
fc23bcf
61e971d
47cb82d
e56dd96
21c3eb6
7e15173
b76e7eb
650468b
639a85b
2b4dbe1
5081cb9
d42026e
9745e5b
ddb4f58
a1ea487
0e41f88
16ffbfb
c6bbe70
59434fa
84e3f11
586eb64
6bbc046
ea39e96
ad8c062
6138266
98bd612
a172027
c46550a
1a56647
80e8c79
4838121
d88a03b
d8291f3
6c9f73c
2c7e147
ef1b473
4819d60
196b2a3
369a4af
dba87a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -653,6 +653,28 @@ def set_line( | |
self.variable.set_line(self.line, self.column, self.end_line, self.end_column) | ||
|
||
|
||
# These specify the kind of a TypeParam | ||
TYPE_VAR_KIND: Final = 0 | ||
PARAM_SPEC_KIND: Final = 1 | ||
TYPE_VAR_TUPLE_KIND: Final = 2 | ||
|
||
|
||
class TypeParam: | ||
__slots__ = ("name", "kind", "upper_bound", "values") | ||
|
||
def __init__( | ||
self, | ||
name: str, | ||
kind: int, | ||
upper_bound: mypy.types.Type | None, | ||
values: list[mypy.types.Type], | ||
) -> None: | ||
self.name = name | ||
self.kind = kind | ||
self.upper_bound = upper_bound | ||
self.values = values | ||
|
||
|
||
FUNCITEM_FLAGS: Final = FUNCBASE_FLAGS + [ | ||
"is_overload", | ||
"is_generator", | ||
|
@@ -672,6 +694,7 @@ class FuncItem(FuncBase): | |
"min_args", # Minimum number of arguments | ||
"max_pos", # Maximum number of positional arguments, -1 if no explicit | ||
# limit (*args not included) | ||
"type_args", # New-style type parameters (PEP 695) | ||
"body", # Body of the function | ||
"is_overload", # Is this an overload variant of function with more than | ||
# one overload variant? | ||
|
@@ -689,12 +712,14 @@ def __init__( | |
arguments: list[Argument] | None = None, | ||
body: Block | None = None, | ||
typ: mypy.types.FunctionLike | None = None, | ||
type_args: list[TypeParam] | None = None, | ||
) -> None: | ||
super().__init__() | ||
self.arguments = arguments or [] | ||
self.arg_names = [None if arg.pos_only else arg.variable.name for arg in self.arguments] | ||
self.arg_kinds: list[ArgKind] = [arg.kind for arg in self.arguments] | ||
self.max_pos: int = self.arg_kinds.count(ARG_POS) + self.arg_kinds.count(ARG_OPT) | ||
self.type_args: list[TypeParam] | None = type_args | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This new field should be serialized. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure we actually need that here. Could be missing something but my understanding is that If it were necessary, mypy would've probably crashed for me while testing the PR with Home Assistant. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might be right, I don't actually understand how our serialization works :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, this is just part of the syntax and doesn't get used beyond semantic analysis. |
||
self.body: Block = body or Block([]) | ||
self.type = typ | ||
self.unanalyzed_type = typ | ||
|
@@ -761,8 +786,9 @@ def __init__( | |
arguments: list[Argument] | None = None, | ||
body: Block | None = None, | ||
typ: mypy.types.FunctionLike | None = None, | ||
type_args: list[TypeParam] | None = None, | ||
) -> None: | ||
super().__init__(arguments, body, typ) | ||
super().__init__(arguments, body, typ, type_args) | ||
self._name = name | ||
self.is_decorated = False | ||
self.is_conditional = False # Defined conditionally (within block)? | ||
|
@@ -1070,6 +1096,7 @@ class ClassDef(Statement): | |
"name", | ||
"_fullname", | ||
"defs", | ||
"type_args", | ||
"type_vars", | ||
"base_type_exprs", | ||
"removed_base_type_exprs", | ||
|
@@ -1089,6 +1116,9 @@ class ClassDef(Statement): | |
name: str # Name of the class without module prefix | ||
_fullname: str # Fully qualified name of the class | ||
defs: Block | ||
# New-style type parameters (PEP 695), unanalyzed | ||
type_args: list[TypeParam] | None | ||
# Semantically analyzed type parameters (all syntax variants) | ||
type_vars: list[mypy.types.TypeVarLikeType] | ||
# Base class expressions (not semantically analyzed -- can be arbitrary expressions) | ||
base_type_exprs: list[Expression] | ||
|
@@ -1111,12 +1141,14 @@ def __init__( | |
base_type_exprs: list[Expression] | None = None, | ||
metaclass: Expression | None = None, | ||
keywords: list[tuple[str, Expression]] | None = None, | ||
type_args: list[TypeParam] | None = None, | ||
) -> None: | ||
super().__init__() | ||
self.name = name | ||
self._fullname = "" | ||
self.defs = defs | ||
self.type_vars = type_vars or [] | ||
self.type_args = type_args | ||
self.base_type_exprs = base_type_exprs or [] | ||
self.removed_base_type_exprs = [] | ||
self.info = CLASSDEF_NO_INFO | ||
|
@@ -1607,6 +1639,25 @@ def accept(self, visitor: StatementVisitor[T]) -> T: | |
return visitor.visit_match_stmt(self) | ||
|
||
|
||
class TypeAliasStmt(Statement): | ||
__slots__ = ("name", "type_args", "value") | ||
|
||
__match_args__ = ("name", "type_args", "value") | ||
|
||
name: NameExpr | ||
type_args: list[TypeParam] | ||
value: Expression # Will get translated into a type | ||
|
||
def __init__(self, name: NameExpr, type_args: list[TypeParam], value: Expression) -> None: | ||
super().__init__() | ||
self.name = name | ||
self.type_args = type_args | ||
self.value = value | ||
|
||
def accept(self, visitor: StatementVisitor[T]) -> T: | ||
return visitor.visit_type_alias_stmt(self) | ||
|
||
|
||
# Expressions | ||
|
||
|
||
|
@@ -2442,6 +2493,7 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: | |
INVARIANT: Final = 0 | ||
COVARIANT: Final = 1 | ||
CONTRAVARIANT: Final = 2 | ||
VARIANCE_NOT_READY: Final = 3 # Variance hasn't been inferred (using Python 3.12 syntax) | ||
|
||
|
||
class TypeVarLikeExpr(SymbolNode, Expression): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use
ast_ParamSpec
andast_TypeVarTuple
here instead, just to prevent accidental name conflicts in the future?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.