@@ -4102,28 +4102,17 @@ def process_typevar_parameters(
41024102 if has_values :
41034103 self .fail ("TypeVar cannot have both values and an upper bound" , context )
41044104 return None
4105- try :
4106- # We want to use our custom error message below, so we suppress
4107- # the default error message for invalid types here.
4108- analyzed = self .expr_to_analyzed_type (
4109- param_value , allow_placeholder = True , report_invalid_types = False
4110- )
4111- if analyzed is None :
4112- # Type variables are special: we need to place them in the symbol table
4113- # soon, even if upper bound is not ready yet. Otherwise avoiding
4114- # a "deadlock" in this common pattern would be tricky:
4115- # T = TypeVar('T', bound=Custom[Any])
4116- # class Custom(Generic[T]):
4117- # ...
4118- analyzed = PlaceholderType (None , [], context .line )
4119- upper_bound = get_proper_type (analyzed )
4120- if isinstance (upper_bound , AnyType ) and upper_bound .is_from_error :
4121- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4122- # Note: we do not return 'None' here -- we want to continue
4123- # using the AnyType as the upper bound.
4124- except TypeTranslationError :
4125- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4105+ tv_arg = self .get_typevarlike_argument ("TypeVar" , param_name , param_value , context )
4106+ if tv_arg is None :
41264107 return None
4108+ upper_bound = tv_arg
4109+ elif param_name == "default" :
4110+ tv_arg = self .get_typevarlike_argument (
4111+ "TypeVar" , param_name , param_value , context , allow_unbound_tvars = True
4112+ )
4113+ if tv_arg is None :
4114+ return None
4115+ default = tv_arg
41274116 elif param_name == "values" :
41284117 # Probably using obsolete syntax with values=(...). Explain the current syntax.
41294118 self .fail ('TypeVar "values" argument not supported' , context )
@@ -4151,6 +4140,50 @@ def process_typevar_parameters(
41514140 variance = INVARIANT
41524141 return variance , upper_bound , default
41534142
4143+ def get_typevarlike_argument (
4144+ self ,
4145+ typevarlike_name : str ,
4146+ param_name : str ,
4147+ param_value : Expression ,
4148+ context : Context ,
4149+ * ,
4150+ allow_unbound_tvars : bool = False ,
4151+ allow_param_spec_literals : bool = False ,
4152+ ) -> ProperType | None :
4153+ try :
4154+ # We want to use our custom error message below, so we suppress
4155+ # the default error message for invalid types here.
4156+ analyzed = self .expr_to_analyzed_type (
4157+ param_value ,
4158+ allow_placeholder = True ,
4159+ report_invalid_types = False ,
4160+ allow_unbound_tvars = allow_unbound_tvars ,
4161+ allow_param_spec_literals = allow_param_spec_literals ,
4162+ )
4163+ if analyzed is None :
4164+ # Type variables are special: we need to place them in the symbol table
4165+ # soon, even if upper bound is not ready yet. Otherwise avoiding
4166+ # a "deadlock" in this common pattern would be tricky:
4167+ # T = TypeVar('T', bound=Custom[Any])
4168+ # class Custom(Generic[T]):
4169+ # ...
4170+ analyzed = PlaceholderType (None , [], context .line )
4171+ typ = get_proper_type (analyzed )
4172+ if isinstance (typ , AnyType ) and typ .is_from_error :
4173+ self .fail (
4174+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4175+ param_value ,
4176+ )
4177+ # Note: we do not return 'None' here -- we want to continue
4178+ # using the AnyType as the upper bound.
4179+ return typ
4180+ except TypeTranslationError :
4181+ self .fail (
4182+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4183+ param_value ,
4184+ )
4185+ return None
4186+
41544187 def extract_typevarlike_name (self , s : AssignmentStmt , call : CallExpr ) -> str | None :
41554188 if not call :
41564189 return None
@@ -4183,13 +4216,47 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool:
41834216 if name is None :
41844217 return False
41854218
4186- # ParamSpec is different from a regular TypeVar:
4187- # arguments are not semantically valid. But, allowed in runtime.
4188- # So, we need to warn users about possible invalid usage.
4189- if len (call .args ) > 1 :
4190- self .fail ("Only the first argument to ParamSpec has defined semantics" , s )
4219+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4220+ if n_values != 0 :
4221+ self .fail ("Only the first positional argument to ParamSpec has defined semantics" , s )
41914222
41924223 default : Type = AnyType (TypeOfAny .from_omitted_generics )
4224+ for param_value , param_name in zip (
4225+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4226+ ):
4227+ if param_name == "default" :
4228+ tv_arg = self .get_typevarlike_argument (
4229+ "ParamSpec" ,
4230+ param_name ,
4231+ param_value ,
4232+ s ,
4233+ allow_unbound_tvars = True ,
4234+ allow_param_spec_literals = True ,
4235+ )
4236+ if tv_arg is None :
4237+ return False
4238+ default = tv_arg
4239+ if isinstance (tv_arg , Parameters ):
4240+ for i , arg_type in enumerate (tv_arg .arg_types ):
4241+ typ = get_proper_type (arg_type )
4242+ if isinstance (typ , AnyType ) and typ .is_from_error :
4243+ self .fail (
4244+ f"Argument { i } of ParamSpec default must be a type" , param_value
4245+ )
4246+ elif not isinstance (default , (AnyType , UnboundType )):
4247+ self .fail (
4248+ "The default argument to ParamSpec must be a tuple expression, ellipsis, or a ParamSpec" ,
4249+ param_value ,
4250+ )
4251+ default = AnyType (TypeOfAny .from_error )
4252+ else :
4253+ # ParamSpec is different from a regular TypeVar:
4254+ # arguments are not semantically valid. But, allowed in runtime.
4255+ # So, we need to warn users about possible invalid usage.
4256+ self .fail (
4257+ "The variance and bound arguments to ParamSpec do not have defined semantics yet" ,
4258+ s ,
4259+ )
41934260
41944261 # PEP 612 reserves the right to define bound, covariant and contravariant arguments to
41954262 # ParamSpec in a later PEP. If and when that happens, we should do something
@@ -4217,10 +4284,34 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool:
42174284 if not call :
42184285 return False
42194286
4220- if len (call .args ) > 1 :
4221- self .fail ("Only the first argument to TypeVarTuple has defined semantics" , s )
4287+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4288+ if n_values != 0 :
4289+ self .fail (
4290+ "Only the first positional argument to TypeVarTuple has defined semantics" , s
4291+ )
42224292
42234293 default : Type = AnyType (TypeOfAny .from_omitted_generics )
4294+ for param_value , param_name in zip (
4295+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4296+ ):
4297+ if param_name == "default" :
4298+ tv_arg = self .get_typevarlike_argument (
4299+ "TypeVarTuple" , param_name , param_value , s , allow_unbound_tvars = True
4300+ )
4301+ if tv_arg is None :
4302+ return False
4303+ default = tv_arg
4304+ if not isinstance (default , UnpackType ):
4305+ self .fail (
4306+ "The default argument to TypeVarTuple must be an Unpacked tuple" ,
4307+ param_value ,
4308+ )
4309+ default = AnyType (TypeOfAny .from_error )
4310+ else :
4311+ self .fail (
4312+ "The variance and bound arguments to TypeVarTuple do not have defined semantics yet" ,
4313+ s ,
4314+ )
42244315
42254316 if not self .incomplete_feature_enabled (TYPE_VAR_TUPLE , s ):
42264317 return False
@@ -6308,6 +6399,8 @@ def expr_to_analyzed_type(
63086399 report_invalid_types : bool = True ,
63096400 allow_placeholder : bool = False ,
63106401 allow_type_any : bool = False ,
6402+ allow_unbound_tvars : bool = False ,
6403+ allow_param_spec_literals : bool = False ,
63116404 ) -> Type | None :
63126405 if isinstance (expr , CallExpr ):
63136406 # This is a legacy syntax intended mostly for Python 2, we keep it for
@@ -6336,6 +6429,8 @@ def expr_to_analyzed_type(
63366429 report_invalid_types = report_invalid_types ,
63376430 allow_placeholder = allow_placeholder ,
63386431 allow_type_any = allow_type_any ,
6432+ allow_unbound_tvars = allow_unbound_tvars ,
6433+ allow_param_spec_literals = allow_param_spec_literals ,
63396434 )
63406435
63416436 def analyze_type_expr (self , expr : Expression ) -> None :
0 commit comments