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