@@ -4098,28 +4098,17 @@ def process_typevar_parameters(
40984098 if has_values :
40994099 self .fail ("TypeVar cannot have both values and an upper bound" , context )
41004100 return None
4101- try :
4102- # We want to use our custom error message below, so we suppress
4103- # the default error message for invalid types here.
4104- analyzed = self .expr_to_analyzed_type (
4105- param_value , allow_placeholder = True , report_invalid_types = False
4106- )
4107- if analyzed is None :
4108- # Type variables are special: we need to place them in the symbol table
4109- # soon, even if upper bound is not ready yet. Otherwise avoiding
4110- # a "deadlock" in this common pattern would be tricky:
4111- # T = TypeVar('T', bound=Custom[Any])
4112- # class Custom(Generic[T]):
4113- # ...
4114- analyzed = PlaceholderType (None , [], context .line )
4115- upper_bound = get_proper_type (analyzed )
4116- if isinstance (upper_bound , AnyType ) and upper_bound .is_from_error :
4117- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4118- # Note: we do not return 'None' here -- we want to continue
4119- # using the AnyType as the upper bound.
4120- except TypeTranslationError :
4121- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4101+ tv_arg = self .get_typevarlike_argument ("TypeVar" , param_name , param_value , context )
4102+ if tv_arg is None :
41224103 return None
4104+ upper_bound = tv_arg
4105+ elif param_name == "default" :
4106+ tv_arg = self .get_typevarlike_argument (
4107+ "TypeVar" , param_name , param_value , context , allow_unbound_tvars = True
4108+ )
4109+ if tv_arg is None :
4110+ return None
4111+ default = tv_arg
41234112 elif param_name == "values" :
41244113 # Probably using obsolete syntax with values=(...). Explain the current syntax.
41254114 self .fail ('TypeVar "values" argument not supported' , context )
@@ -4147,6 +4136,50 @@ def process_typevar_parameters(
41474136 variance = INVARIANT
41484137 return variance , upper_bound , default
41494138
4139+ def get_typevarlike_argument (
4140+ self ,
4141+ typevarlike_name : str ,
4142+ param_name : str ,
4143+ param_value : Expression ,
4144+ context : Context ,
4145+ * ,
4146+ allow_unbound_tvars : bool = False ,
4147+ allow_param_spec_literals : bool = False ,
4148+ ) -> ProperType | None :
4149+ try :
4150+ # We want to use our custom error message below, so we suppress
4151+ # the default error message for invalid types here.
4152+ analyzed = self .expr_to_analyzed_type (
4153+ param_value ,
4154+ allow_placeholder = True ,
4155+ report_invalid_types = False ,
4156+ allow_unbound_tvars = allow_unbound_tvars ,
4157+ allow_param_spec_literals = allow_param_spec_literals ,
4158+ )
4159+ if analyzed is None :
4160+ # Type variables are special: we need to place them in the symbol table
4161+ # soon, even if upper bound is not ready yet. Otherwise avoiding
4162+ # a "deadlock" in this common pattern would be tricky:
4163+ # T = TypeVar('T', bound=Custom[Any])
4164+ # class Custom(Generic[T]):
4165+ # ...
4166+ analyzed = PlaceholderType (None , [], context .line )
4167+ typ = get_proper_type (analyzed )
4168+ if isinstance (typ , AnyType ) and typ .is_from_error :
4169+ self .fail (
4170+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4171+ param_value ,
4172+ )
4173+ # Note: we do not return 'None' here -- we want to continue
4174+ # using the AnyType as the upper bound.
4175+ return typ
4176+ except TypeTranslationError :
4177+ self .fail (
4178+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4179+ param_value ,
4180+ )
4181+ return None
4182+
41504183 def extract_typevarlike_name (self , s : AssignmentStmt , call : CallExpr ) -> str | None :
41514184 if not call :
41524185 return None
@@ -4179,13 +4212,47 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool:
41794212 if name is None :
41804213 return False
41814214
4182- # ParamSpec is different from a regular TypeVar:
4183- # arguments are not semantically valid. But, allowed in runtime.
4184- # So, we need to warn users about possible invalid usage.
4185- if len (call .args ) > 1 :
4186- self .fail ("Only the first argument to ParamSpec has defined semantics" , s )
4215+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4216+ if n_values != 0 :
4217+ self .fail ("Only the first positional argument to ParamSpec has defined semantics" , s )
41874218
41884219 default : Type = AnyType (TypeOfAny .from_omitted_generics )
4220+ for param_value , param_name in zip (
4221+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4222+ ):
4223+ if param_name == "default" :
4224+ tv_arg = self .get_typevarlike_argument (
4225+ "ParamSpec" ,
4226+ param_name ,
4227+ param_value ,
4228+ s ,
4229+ allow_unbound_tvars = True ,
4230+ allow_param_spec_literals = True ,
4231+ )
4232+ if tv_arg is None :
4233+ return False
4234+ default = tv_arg
4235+ if isinstance (tv_arg , Parameters ):
4236+ for i , arg_type in enumerate (tv_arg .arg_types ):
4237+ typ = get_proper_type (arg_type )
4238+ if isinstance (typ , AnyType ) and typ .is_from_error :
4239+ self .fail (
4240+ f"Argument { i } of ParamSpec default must be a type" , param_value
4241+ )
4242+ elif not isinstance (default , (AnyType , UnboundType )):
4243+ self .fail (
4244+ "The default argument to ParamSpec must be a tuple expression, ellipsis, or a ParamSpec" ,
4245+ param_value ,
4246+ )
4247+ default = AnyType (TypeOfAny .from_error )
4248+ else :
4249+ # ParamSpec is different from a regular TypeVar:
4250+ # arguments are not semantically valid. But, allowed in runtime.
4251+ # So, we need to warn users about possible invalid usage.
4252+ self .fail (
4253+ "The variance and bound arguments to ParamSpec do not have defined semantics yet" ,
4254+ s ,
4255+ )
41894256
41904257 # PEP 612 reserves the right to define bound, covariant and contravariant arguments to
41914258 # ParamSpec in a later PEP. If and when that happens, we should do something
@@ -4213,10 +4280,34 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool:
42134280 if not call :
42144281 return False
42154282
4216- if len (call .args ) > 1 :
4217- self .fail ("Only the first argument to TypeVarTuple has defined semantics" , s )
4283+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4284+ if n_values != 0 :
4285+ self .fail (
4286+ "Only the first positional argument to TypeVarTuple has defined semantics" , s
4287+ )
42184288
42194289 default : Type = AnyType (TypeOfAny .from_omitted_generics )
4290+ for param_value , param_name in zip (
4291+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4292+ ):
4293+ if param_name == "default" :
4294+ tv_arg = self .get_typevarlike_argument (
4295+ "TypeVarTuple" , param_name , param_value , s , allow_unbound_tvars = True
4296+ )
4297+ if tv_arg is None :
4298+ return False
4299+ default = tv_arg
4300+ if not isinstance (default , UnpackType ):
4301+ self .fail (
4302+ "The default argument to TypeVarTuple must be an Unpacked tuple" ,
4303+ param_value ,
4304+ )
4305+ default = AnyType (TypeOfAny .from_error )
4306+ else :
4307+ self .fail (
4308+ "The variance and bound arguments to TypeVarTuple do not have defined semantics yet" ,
4309+ s ,
4310+ )
42204311
42214312 if not self .incomplete_feature_enabled (TYPE_VAR_TUPLE , s ):
42224313 return False
@@ -6310,6 +6401,8 @@ def expr_to_analyzed_type(
63106401 report_invalid_types : bool = True ,
63116402 allow_placeholder : bool = False ,
63126403 allow_type_any : bool = False ,
6404+ allow_unbound_tvars : bool = False ,
6405+ allow_param_spec_literals : bool = False ,
63136406 ) -> Type | None :
63146407 if isinstance (expr , CallExpr ):
63156408 # This is a legacy syntax intended mostly for Python 2, we keep it for
@@ -6338,6 +6431,8 @@ def expr_to_analyzed_type(
63386431 report_invalid_types = report_invalid_types ,
63396432 allow_placeholder = allow_placeholder ,
63406433 allow_type_any = allow_type_any ,
6434+ allow_unbound_tvars = allow_unbound_tvars ,
6435+ allow_param_spec_literals = allow_param_spec_literals ,
63416436 )
63426437
63436438 def analyze_type_expr (self , expr : Expression ) -> None :
0 commit comments