@@ -4096,12 +4096,14 @@ def process_typevar_parameters(
40964096 if has_values :
40974097 self .fail ("TypeVar cannot have both values and an upper bound" , context )
40984098 return None
4099- tv_arg = self .get_typevarlike_argument (param_name , param_value , context )
4099+ tv_arg = self .get_typevarlike_argument ("TypeVar" , param_name , param_value , context )
41004100 if tv_arg is None :
41014101 return None
41024102 upper_bound = tv_arg
41034103 elif param_name == "default" :
4104- tv_arg = self .get_typevarlike_argument (param_name , param_value , context )
4104+ tv_arg = self .get_typevarlike_argument (
4105+ "TypeVar" , param_name , param_value , context , allow_unbound_tvars = True
4106+ )
41054107 if tv_arg is None :
41064108 return None
41074109 default = tv_arg
@@ -4133,13 +4135,24 @@ def process_typevar_parameters(
41334135 return variance , upper_bound , default
41344136
41354137 def get_typevarlike_argument (
4136- self , param_name : str , param_value : Expression , context : Context
4138+ self ,
4139+ typevarlike_name : str ,
4140+ param_name : str ,
4141+ param_value : Expression ,
4142+ context : Context ,
4143+ * ,
4144+ allow_unbound_tvars : bool = False ,
4145+ allow_param_spec_literals : bool = False ,
41374146 ) -> ProperType | None :
41384147 try :
41394148 # We want to use our custom error message below, so we suppress
41404149 # the default error message for invalid types here.
41414150 analyzed = self .expr_to_analyzed_type (
4142- param_value , allow_placeholder = True , report_invalid_types = False
4151+ param_value ,
4152+ allow_placeholder = True ,
4153+ report_invalid_types = False ,
4154+ allow_unbound_tvars = allow_unbound_tvars ,
4155+ allow_param_spec_literals = allow_param_spec_literals ,
41434156 )
41444157 if analyzed is None :
41454158 # Type variables are special: we need to place them in the symbol table
@@ -4152,13 +4165,17 @@ def get_typevarlike_argument(
41524165 typ = get_proper_type (analyzed )
41534166 if isinstance (typ , AnyType ) and typ .is_from_error :
41544167 self .fail (
4155- message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (param_name ), param_value
4168+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4169+ param_value ,
41564170 )
41574171 # Note: we do not return 'None' here -- we want to continue
41584172 # using the AnyType as the upper bound.
41594173 return typ
41604174 except TypeTranslationError :
4161- self .fail (message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (param_name ), param_value )
4175+ self .fail (
4176+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4177+ param_value ,
4178+ )
41624179 return None
41634180
41644181 def extract_typevarlike_name (self , s : AssignmentStmt , call : CallExpr ) -> str | None :
@@ -4202,10 +4219,30 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool:
42024219 call .args [1 + n_values :], call .arg_names [1 + n_values :]
42034220 ):
42044221 if param_name == "default" :
4205- tv_arg = self .get_typevarlike_argument (param_name , param_value , s )
4222+ tv_arg = self .get_typevarlike_argument (
4223+ "ParamSpec" ,
4224+ param_name ,
4225+ param_value ,
4226+ s ,
4227+ allow_unbound_tvars = True ,
4228+ allow_param_spec_literals = True ,
4229+ )
42064230 if tv_arg is None :
42074231 return False
42084232 default = tv_arg
4233+ if isinstance (tv_arg , Parameters ):
4234+ for i , arg_type in enumerate (tv_arg .arg_types ):
4235+ typ = get_proper_type (arg_type )
4236+ if isinstance (typ , AnyType ) and typ .is_from_error :
4237+ self .fail (
4238+ f"Argument { i } of ParamSpec default must be a type" , param_value
4239+ )
4240+ elif not isinstance (default , (AnyType , UnboundType )):
4241+ self .fail (
4242+ "The default argument to ParamSpec must be a tuple expression, ellipsis, or a ParamSpec" ,
4243+ param_value ,
4244+ )
4245+ default = AnyType (TypeOfAny .from_error )
42094246 else :
42104247 # ParamSpec is different from a regular TypeVar:
42114248 # arguments are not semantically valid. But, allowed in runtime.
@@ -4252,15 +4289,18 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool:
42524289 call .args [1 + n_values :], call .arg_names [1 + n_values :]
42534290 ):
42544291 if param_name == "default" :
4255- tv_arg = self .get_typevarlike_argument (param_name , param_value , s )
4292+ tv_arg = self .get_typevarlike_argument (
4293+ "TypeVarTuple" , param_name , param_value , s , allow_unbound_tvars = True
4294+ )
42564295 if tv_arg is None :
42574296 return False
42584297 default = tv_arg
42594298 if not isinstance (default , UnpackType ):
42604299 self .fail (
4261- "The default argument to TypeVarTuple must be an Unpacked tuple" , default
4300+ "The default argument to TypeVarTuple must be an Unpacked tuple" ,
4301+ param_value ,
42624302 )
4263- return False
4303+ default = AnyType ( TypeOfAny . from_error )
42644304 else :
42654305 self .fail (
42664306 "The variance and bound arguments to TypeVarTuple do not have defined semantics yet" ,
@@ -6359,6 +6399,8 @@ def expr_to_analyzed_type(
63596399 report_invalid_types : bool = True ,
63606400 allow_placeholder : bool = False ,
63616401 allow_type_any : bool = False ,
6402+ allow_unbound_tvars : bool = False ,
6403+ allow_param_spec_literals : bool = False ,
63626404 ) -> Type | None :
63636405 if isinstance (expr , CallExpr ):
63646406 # This is a legacy syntax intended mostly for Python 2, we keep it for
@@ -6387,6 +6429,8 @@ def expr_to_analyzed_type(
63876429 report_invalid_types = report_invalid_types ,
63886430 allow_placeholder = allow_placeholder ,
63896431 allow_type_any = allow_type_any ,
6432+ allow_unbound_tvars = allow_unbound_tvars ,
6433+ allow_param_spec_literals = allow_param_spec_literals ,
63906434 )
63916435
63926436 def analyze_type_expr (self , expr : Expression ) -> None :
0 commit comments