@@ -4096,28 +4096,17 @@ 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- try :
4100- # We want to use our custom error message below, so we suppress
4101- # the default error message for invalid types here.
4102- analyzed = self .expr_to_analyzed_type (
4103- param_value , allow_placeholder = True , report_invalid_types = False
4104- )
4105- if analyzed is None :
4106- # Type variables are special: we need to place them in the symbol table
4107- # soon, even if upper bound is not ready yet. Otherwise avoiding
4108- # a "deadlock" in this common pattern would be tricky:
4109- # T = TypeVar('T', bound=Custom[Any])
4110- # class Custom(Generic[T]):
4111- # ...
4112- analyzed = PlaceholderType (None , [], context .line )
4113- upper_bound = get_proper_type (analyzed )
4114- if isinstance (upper_bound , AnyType ) and upper_bound .is_from_error :
4115- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4116- # Note: we do not return 'None' here -- we want to continue
4117- # using the AnyType as the upper bound.
4118- except TypeTranslationError :
4119- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4099+ tv_arg = self .get_typevarlike_argument ("TypeVar" , param_name , param_value , context )
4100+ if tv_arg is None :
41204101 return None
4102+ upper_bound = tv_arg
4103+ elif param_name == "default" :
4104+ tv_arg = self .get_typevarlike_argument (
4105+ "TypeVar" , param_name , param_value , context , allow_unbound_tvars = True
4106+ )
4107+ if tv_arg is None :
4108+ return None
4109+ default = tv_arg
41214110 elif param_name == "values" :
41224111 # Probably using obsolete syntax with values=(...). Explain the current syntax.
41234112 self .fail ('TypeVar "values" argument not supported' , context )
@@ -4145,6 +4134,50 @@ def process_typevar_parameters(
41454134 variance = INVARIANT
41464135 return variance , upper_bound , default
41474136
4137+ def get_typevarlike_argument (
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 ,
4146+ ) -> ProperType | None :
4147+ try :
4148+ # We want to use our custom error message below, so we suppress
4149+ # the default error message for invalid types here.
4150+ analyzed = self .expr_to_analyzed_type (
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 ,
4156+ )
4157+ if analyzed is None :
4158+ # Type variables are special: we need to place them in the symbol table
4159+ # soon, even if upper bound is not ready yet. Otherwise avoiding
4160+ # a "deadlock" in this common pattern would be tricky:
4161+ # T = TypeVar('T', bound=Custom[Any])
4162+ # class Custom(Generic[T]):
4163+ # ...
4164+ analyzed = PlaceholderType (None , [], context .line )
4165+ typ = get_proper_type (analyzed )
4166+ if isinstance (typ , AnyType ) and typ .is_from_error :
4167+ self .fail (
4168+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4169+ param_value ,
4170+ )
4171+ # Note: we do not return 'None' here -- we want to continue
4172+ # using the AnyType as the upper bound.
4173+ return typ
4174+ except TypeTranslationError :
4175+ self .fail (
4176+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4177+ param_value ,
4178+ )
4179+ return None
4180+
41484181 def extract_typevarlike_name (self , s : AssignmentStmt , call : CallExpr ) -> str | None :
41494182 if not call :
41504183 return None
@@ -4177,13 +4210,47 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool:
41774210 if name is None :
41784211 return False
41794212
4180- # ParamSpec is different from a regular TypeVar:
4181- # arguments are not semantically valid. But, allowed in runtime.
4182- # So, we need to warn users about possible invalid usage.
4183- if len (call .args ) > 1 :
4184- self .fail ("Only the first argument to ParamSpec has defined semantics" , s )
4213+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4214+ if n_values != 0 :
4215+ self .fail ("Only the first positional argument to ParamSpec has defined semantics" , s )
41854216
41864217 default : Type = AnyType (TypeOfAny .from_omitted_generics )
4218+ for param_value , param_name in zip (
4219+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4220+ ):
4221+ if param_name == "default" :
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+ )
4230+ if tv_arg is None :
4231+ return False
4232+ 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 )
4246+ else :
4247+ # ParamSpec is different from a regular TypeVar:
4248+ # arguments are not semantically valid. But, allowed in runtime.
4249+ # So, we need to warn users about possible invalid usage.
4250+ self .fail (
4251+ "The variance and bound arguments to ParamSpec do not have defined semantics yet" ,
4252+ s ,
4253+ )
41874254
41884255 # PEP 612 reserves the right to define bound, covariant and contravariant arguments to
41894256 # ParamSpec in a later PEP. If and when that happens, we should do something
@@ -4211,10 +4278,34 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool:
42114278 if not call :
42124279 return False
42134280
4214- if len (call .args ) > 1 :
4215- self .fail ("Only the first argument to TypeVarTuple has defined semantics" , s )
4281+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4282+ if n_values != 0 :
4283+ self .fail (
4284+ "Only the first positional argument to TypeVarTuple has defined semantics" , s
4285+ )
42164286
42174287 default : Type = AnyType (TypeOfAny .from_omitted_generics )
4288+ for param_value , param_name in zip (
4289+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4290+ ):
4291+ if param_name == "default" :
4292+ tv_arg = self .get_typevarlike_argument (
4293+ "TypeVarTuple" , param_name , param_value , s , allow_unbound_tvars = True
4294+ )
4295+ if tv_arg is None :
4296+ return False
4297+ default = tv_arg
4298+ if not isinstance (default , UnpackType ):
4299+ self .fail (
4300+ "The default argument to TypeVarTuple must be an Unpacked tuple" ,
4301+ param_value ,
4302+ )
4303+ default = AnyType (TypeOfAny .from_error )
4304+ else :
4305+ self .fail (
4306+ "The variance and bound arguments to TypeVarTuple do not have defined semantics yet" ,
4307+ s ,
4308+ )
42184309
42194310 if not self .incomplete_feature_enabled (TYPE_VAR_TUPLE , s ):
42204311 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