76
76
nongen_builtins , get_member_expr_fullname , REVEAL_TYPE ,
77
77
REVEAL_LOCALS , is_final_node , TypedDictExpr , type_aliases_target_versions ,
78
78
EnumCallExpr , RUNTIME_PROTOCOL_DECOS , FakeExpression , Statement , AssignmentExpr ,
79
+ ParamSpecExpr
79
80
)
80
81
from mypy .tvar_scope import TypeVarScope
81
82
from mypy .typevars import fill_typevars
@@ -1921,6 +1922,8 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
1921
1922
# * type variable definition
1922
1923
elif self .process_typevar_declaration (s ):
1923
1924
special_form = True
1925
+ elif self .process_paramspec_declaration (s ):
1926
+ special_form = True
1924
1927
# * type constructors
1925
1928
elif self .analyze_namedtuple_assign (s ):
1926
1929
special_form = True
@@ -2836,7 +2839,7 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool:
2836
2839
Return True if this looks like a type variable declaration (but maybe
2837
2840
with errors), otherwise return False.
2838
2841
"""
2839
- call = self .get_typevar_declaration ( s )
2842
+ call = self .get_typevarlike_declaration ( s , ( "typing.TypeVar" ,) )
2840
2843
if not call :
2841
2844
return False
2842
2845
@@ -2847,7 +2850,7 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool:
2847
2850
return False
2848
2851
2849
2852
name = lvalue .name
2850
- if not self .check_typevar_name (call , name , s ):
2853
+ if not self .check_typevarlike_name (call , name , s ):
2851
2854
return False
2852
2855
2853
2856
# Constraining types
@@ -2907,24 +2910,31 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool:
2907
2910
self .add_symbol (name , call .analyzed , s )
2908
2911
return True
2909
2912
2910
- def check_typevar_name (self , call : CallExpr , name : str , context : Context ) -> bool :
2913
+ def check_typevarlike_name (self , call : CallExpr , name : str , context : Context ) -> bool :
2914
+ """Checks that the name of a TypeVar or ParamSpec matches its variable."""
2911
2915
name = unmangle (name )
2916
+ assert isinstance (call .callee , RefExpr )
2917
+ typevarlike_type = (
2918
+ call .callee .name if isinstance (call .callee , NameExpr ) else call .callee .fullname
2919
+ )
2912
2920
if len (call .args ) < 1 :
2913
- self .fail ("Too few arguments for TypeVar ()" , context )
2921
+ self .fail ("Too few arguments for {} ()" . format ( typevarlike_type ) , context )
2914
2922
return False
2915
2923
if (not isinstance (call .args [0 ], (StrExpr , BytesExpr , UnicodeExpr ))
2916
2924
or not call .arg_kinds [0 ] == ARG_POS ):
2917
- self .fail ("TypeVar() expects a string literal as first argument" , context )
2925
+ self .fail ("{}() expects a string literal as first argument" .format (typevarlike_type ),
2926
+ context )
2918
2927
return False
2919
2928
elif call .args [0 ].value != name :
2920
- msg = "String argument 1 '{}' to TypeVar (...) does not match variable name '{}'"
2921
- self .fail (msg .format (call .args [0 ].value , name ), context )
2929
+ msg = "String argument 1 '{}' to {} (...) does not match variable name '{}'"
2930
+ self .fail (msg .format (call .args [0 ].value , typevarlike_type , name ), context )
2922
2931
return False
2923
2932
return True
2924
2933
2925
- def get_typevar_declaration (self , s : AssignmentStmt ) -> Optional [CallExpr ]:
2926
- """Returns the TypeVar() call expression if `s` is a type var declaration
2927
- or None otherwise.
2934
+ def get_typevarlike_declaration (self , s : AssignmentStmt ,
2935
+ typevarlike_types : Tuple [str , ...]) -> Optional [CallExpr ]:
2936
+ """Returns the call expression if `s` is a declaration of `typevarlike_type`
2937
+ (TypeVar or ParamSpec), or None otherwise.
2928
2938
"""
2929
2939
if len (s .lvalues ) != 1 or not isinstance (s .lvalues [0 ], NameExpr ):
2930
2940
return None
@@ -2934,7 +2944,7 @@ def get_typevar_declaration(self, s: AssignmentStmt) -> Optional[CallExpr]:
2934
2944
callee = call .callee
2935
2945
if not isinstance (callee , RefExpr ):
2936
2946
return None
2937
- if callee .fullname != 'typing.TypeVar' :
2947
+ if callee .fullname not in typevarlike_types :
2938
2948
return None
2939
2949
return call
2940
2950
@@ -3021,6 +3031,41 @@ def process_typevar_parameters(self, args: List[Expression],
3021
3031
variance = INVARIANT
3022
3032
return variance , upper_bound
3023
3033
3034
+ def process_paramspec_declaration (self , s : AssignmentStmt ) -> bool :
3035
+ """Checks if s declares a ParamSpec; if yes, store it in symbol table.
3036
+
3037
+ Return True if this looks like a ParamSpec (maybe with errors), otherwise return False.
3038
+
3039
+ In the future, ParamSpec may accept bounds and variance arguments, in which
3040
+ case more aggressive sharing of code with process_typevar_declaration should be pursued.
3041
+ """
3042
+ call = self .get_typevarlike_declaration (
3043
+ s , ("typing_extensions.ParamSpec" , "typing.ParamSpec" )
3044
+ )
3045
+ if not call :
3046
+ return False
3047
+
3048
+ lvalue = s .lvalues [0 ]
3049
+ assert isinstance (lvalue , NameExpr )
3050
+ if s .type :
3051
+ self .fail ("Cannot declare the type of a parameter specification" , s )
3052
+ return False
3053
+
3054
+ name = lvalue .name
3055
+ if not self .check_typevarlike_name (call , name , s ):
3056
+ return False
3057
+
3058
+ # PEP 612 reserves the right to define bound, covariant and contravariant arguments to
3059
+ # ParamSpec in a later PEP. If and when that happens, we should do something
3060
+ # on the lines of process_typevar_parameters
3061
+ paramspec_var = ParamSpecExpr (
3062
+ name , self .qualified_name (name ), self .object_type (), INVARIANT
3063
+ )
3064
+ paramspec_var .line = call .line
3065
+ call .analyzed = paramspec_var
3066
+ self .add_symbol (name , call .analyzed , s )
3067
+ return True
3068
+
3024
3069
def basic_new_typeinfo (self , name : str , basetype_or_fallback : Instance ) -> TypeInfo :
3025
3070
class_def = ClassDef (name , Block ([]))
3026
3071
if self .is_func_scope () and not self .type :
0 commit comments