@@ -4135,28 +4135,15 @@ def process_typevar_parameters(
41354135 if has_values :
41364136 self .fail ("TypeVar cannot have both values and an upper bound" , context )
41374137 return None
4138- try :
4139- # We want to use our custom error message below, so we suppress
4140- # the default error message for invalid types here.
4141- analyzed = self .expr_to_analyzed_type (
4142- param_value , allow_placeholder = True , report_invalid_types = False
4143- )
4144- if analyzed is None :
4145- # Type variables are special: we need to place them in the symbol table
4146- # soon, even if upper bound is not ready yet. Otherwise avoiding
4147- # a "deadlock" in this common pattern would be tricky:
4148- # T = TypeVar('T', bound=Custom[Any])
4149- # class Custom(Generic[T]):
4150- # ...
4151- analyzed = PlaceholderType (None , [], context .line )
4152- upper_bound = get_proper_type (analyzed )
4153- if isinstance (upper_bound , AnyType ) and upper_bound .is_from_error :
4154- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4155- # Note: we do not return 'None' here -- we want to continue
4156- # using the AnyType as the upper bound.
4157- except TypeTranslationError :
4158- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4138+ tv_arg = self .get_typevarlike_argument ("TypeVar" , param_name , param_value , context )
4139+ if tv_arg is None :
41594140 return None
4141+ upper_bound = tv_arg
4142+ elif param_name == "default" :
4143+ tv_arg = self .get_typevarlike_argument (
4144+ "TypeVar" , param_name , param_value , context , allow_unbound_tvars = True
4145+ )
4146+ default = tv_arg or AnyType (TypeOfAny .from_error )
41604147 elif param_name == "values" :
41614148 # Probably using obsolete syntax with values=(...). Explain the current syntax.
41624149 self .fail ('TypeVar "values" argument not supported' , context )
@@ -4184,6 +4171,52 @@ def process_typevar_parameters(
41844171 variance = INVARIANT
41854172 return variance , upper_bound , default
41864173
4174+ def get_typevarlike_argument (
4175+ self ,
4176+ typevarlike_name : str ,
4177+ param_name : str ,
4178+ param_value : Expression ,
4179+ context : Context ,
4180+ * ,
4181+ allow_unbound_tvars : bool = False ,
4182+ allow_param_spec_literals : bool = False ,
4183+ report_invalid_typevar_arg : bool = True ,
4184+ ) -> ProperType | None :
4185+ try :
4186+ # We want to use our custom error message below, so we suppress
4187+ # the default error message for invalid types here.
4188+ analyzed = self .expr_to_analyzed_type (
4189+ param_value ,
4190+ allow_placeholder = True ,
4191+ report_invalid_types = False ,
4192+ allow_unbound_tvars = allow_unbound_tvars ,
4193+ allow_param_spec_literals = allow_param_spec_literals ,
4194+ )
4195+ if analyzed is None :
4196+ # Type variables are special: we need to place them in the symbol table
4197+ # soon, even if upper bound is not ready yet. Otherwise avoiding
4198+ # a "deadlock" in this common pattern would be tricky:
4199+ # T = TypeVar('T', bound=Custom[Any])
4200+ # class Custom(Generic[T]):
4201+ # ...
4202+ analyzed = PlaceholderType (None , [], context .line )
4203+ typ = get_proper_type (analyzed )
4204+ if report_invalid_typevar_arg and isinstance (typ , AnyType ) and typ .is_from_error :
4205+ self .fail (
4206+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4207+ param_value ,
4208+ )
4209+ # Note: we do not return 'None' here -- we want to continue
4210+ # using the AnyType.
4211+ return typ
4212+ except TypeTranslationError :
4213+ if report_invalid_typevar_arg :
4214+ self .fail (
4215+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4216+ param_value ,
4217+ )
4218+ return None
4219+
41874220 def extract_typevarlike_name (self , s : AssignmentStmt , call : CallExpr ) -> str | None :
41884221 if not call :
41894222 return None
@@ -4216,13 +4249,50 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool:
42164249 if name is None :
42174250 return False
42184251
4219- # ParamSpec is different from a regular TypeVar:
4220- # arguments are not semantically valid. But, allowed in runtime.
4221- # So, we need to warn users about possible invalid usage.
4222- if len (call .args ) > 1 :
4223- self .fail ("Only the first argument to ParamSpec has defined semantics" , s )
4252+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4253+ if n_values != 0 :
4254+ self .fail ('Too many positional arguments for "ParamSpec"' , s )
42244255
42254256 default : Type = AnyType (TypeOfAny .from_omitted_generics )
4257+ for param_value , param_name in zip (
4258+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4259+ ):
4260+ if param_name == "default" :
4261+ tv_arg = self .get_typevarlike_argument (
4262+ "ParamSpec" ,
4263+ param_name ,
4264+ param_value ,
4265+ s ,
4266+ allow_unbound_tvars = True ,
4267+ allow_param_spec_literals = True ,
4268+ report_invalid_typevar_arg = False ,
4269+ )
4270+ default = tv_arg or AnyType (TypeOfAny .from_error )
4271+ if isinstance (tv_arg , Parameters ):
4272+ for i , arg_type in enumerate (tv_arg .arg_types ):
4273+ typ = get_proper_type (arg_type )
4274+ if isinstance (typ , AnyType ) and typ .is_from_error :
4275+ self .fail (
4276+ f"Argument { i } of ParamSpec default must be a type" , param_value
4277+ )
4278+ elif (
4279+ isinstance (default , AnyType )
4280+ and default .is_from_error
4281+ or not isinstance (default , (AnyType , UnboundType ))
4282+ ):
4283+ self .fail (
4284+ "The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec" ,
4285+ param_value ,
4286+ )
4287+ default = AnyType (TypeOfAny .from_error )
4288+ else :
4289+ # ParamSpec is different from a regular TypeVar:
4290+ # arguments are not semantically valid. But, allowed in runtime.
4291+ # So, we need to warn users about possible invalid usage.
4292+ self .fail (
4293+ "The variance and bound arguments to ParamSpec do not have defined semantics yet" ,
4294+ s ,
4295+ )
42264296
42274297 # PEP 612 reserves the right to define bound, covariant and contravariant arguments to
42284298 # ParamSpec in a later PEP. If and when that happens, we should do something
@@ -4256,10 +4326,32 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool:
42564326 if not call :
42574327 return False
42584328
4259- if len (call .args ) > 1 :
4260- self .fail ("Only the first argument to TypeVarTuple has defined semantics" , s )
4329+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4330+ if n_values != 0 :
4331+ self .fail ('Too many positional arguments for "TypeVarTuple"' , s )
42614332
42624333 default : Type = AnyType (TypeOfAny .from_omitted_generics )
4334+ for param_value , param_name in zip (
4335+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4336+ ):
4337+ if param_name == "default" :
4338+ tv_arg = self .get_typevarlike_argument (
4339+ "TypeVarTuple" ,
4340+ param_name ,
4341+ param_value ,
4342+ s ,
4343+ allow_unbound_tvars = True ,
4344+ report_invalid_typevar_arg = False ,
4345+ )
4346+ default = tv_arg or AnyType (TypeOfAny .from_error )
4347+ if not isinstance (default , UnpackType ):
4348+ self .fail (
4349+ "The default argument to TypeVarTuple must be an Unpacked tuple" ,
4350+ param_value ,
4351+ )
4352+ default = AnyType (TypeOfAny .from_error )
4353+ else :
4354+ self .fail (f'Unexpected keyword argument "{ param_name } " for "TypeVarTuple"' , s )
42634355
42644356 if not self .incomplete_feature_enabled (TYPE_VAR_TUPLE , s ):
42654357 return False
@@ -6359,6 +6451,8 @@ def expr_to_analyzed_type(
63596451 report_invalid_types : bool = True ,
63606452 allow_placeholder : bool = False ,
63616453 allow_type_any : bool = False ,
6454+ allow_unbound_tvars : bool = False ,
6455+ allow_param_spec_literals : bool = False ,
63626456 ) -> Type | None :
63636457 if isinstance (expr , CallExpr ):
63646458 # This is a legacy syntax intended mostly for Python 2, we keep it for
@@ -6387,6 +6481,8 @@ def expr_to_analyzed_type(
63876481 report_invalid_types = report_invalid_types ,
63886482 allow_placeholder = allow_placeholder ,
63896483 allow_type_any = allow_type_any ,
6484+ allow_unbound_tvars = allow_unbound_tvars ,
6485+ allow_param_spec_literals = allow_param_spec_literals ,
63906486 )
63916487
63926488 def analyze_type_expr (self , expr : Expression ) -> None :
0 commit comments