@@ -4106,28 +4106,17 @@ def process_typevar_parameters(
41064106 if has_values :
41074107 self .fail ("TypeVar cannot have both values and an upper bound" , context )
41084108 return None
4109- try :
4110- # We want to use our custom error message below, so we suppress
4111- # the default error message for invalid types here.
4112- analyzed = self .expr_to_analyzed_type (
4113- param_value , allow_placeholder = True , report_invalid_types = False
4114- )
4115- if analyzed is None :
4116- # Type variables are special: we need to place them in the symbol table
4117- # soon, even if upper bound is not ready yet. Otherwise avoiding
4118- # a "deadlock" in this common pattern would be tricky:
4119- # T = TypeVar('T', bound=Custom[Any])
4120- # class Custom(Generic[T]):
4121- # ...
4122- analyzed = PlaceholderType (None , [], context .line )
4123- upper_bound = get_proper_type (analyzed )
4124- if isinstance (upper_bound , AnyType ) and upper_bound .is_from_error :
4125- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4126- # Note: we do not return 'None' here -- we want to continue
4127- # using the AnyType as the upper bound.
4128- except TypeTranslationError :
4129- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4109+ tv_arg = self .get_typevarlike_argument ("TypeVar" , param_name , param_value , context )
4110+ if tv_arg is None :
41304111 return None
4112+ upper_bound = tv_arg
4113+ elif param_name == "default" :
4114+ tv_arg = self .get_typevarlike_argument (
4115+ "TypeVar" , param_name , param_value , context , allow_unbound_tvars = True
4116+ )
4117+ if tv_arg is None :
4118+ return None
4119+ default = tv_arg
41314120 elif param_name == "values" :
41324121 # Probably using obsolete syntax with values=(...). Explain the current syntax.
41334122 self .fail ('TypeVar "values" argument not supported' , context )
@@ -4155,6 +4144,50 @@ def process_typevar_parameters(
41554144 variance = INVARIANT
41564145 return variance , upper_bound , default
41574146
4147+ def get_typevarlike_argument (
4148+ self ,
4149+ typevarlike_name : str ,
4150+ param_name : str ,
4151+ param_value : Expression ,
4152+ context : Context ,
4153+ * ,
4154+ allow_unbound_tvars : bool = False ,
4155+ allow_param_spec_literals : bool = False ,
4156+ ) -> ProperType | None :
4157+ try :
4158+ # We want to use our custom error message below, so we suppress
4159+ # the default error message for invalid types here.
4160+ analyzed = self .expr_to_analyzed_type (
4161+ param_value ,
4162+ allow_placeholder = True ,
4163+ report_invalid_types = False ,
4164+ allow_unbound_tvars = allow_unbound_tvars ,
4165+ allow_param_spec_literals = allow_param_spec_literals ,
4166+ )
4167+ if analyzed is None :
4168+ # Type variables are special: we need to place them in the symbol table
4169+ # soon, even if upper bound is not ready yet. Otherwise avoiding
4170+ # a "deadlock" in this common pattern would be tricky:
4171+ # T = TypeVar('T', bound=Custom[Any])
4172+ # class Custom(Generic[T]):
4173+ # ...
4174+ analyzed = PlaceholderType (None , [], context .line )
4175+ typ = get_proper_type (analyzed )
4176+ if isinstance (typ , AnyType ) and typ .is_from_error :
4177+ self .fail (
4178+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4179+ param_value ,
4180+ )
4181+ # Note: we do not return 'None' here -- we want to continue
4182+ # using the AnyType as the upper bound.
4183+ return typ
4184+ except TypeTranslationError :
4185+ self .fail (
4186+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4187+ param_value ,
4188+ )
4189+ return None
4190+
41584191 def extract_typevarlike_name (self , s : AssignmentStmt , call : CallExpr ) -> str | None :
41594192 if not call :
41604193 return None
@@ -4187,13 +4220,47 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool:
41874220 if name is None :
41884221 return False
41894222
4190- # ParamSpec is different from a regular TypeVar:
4191- # arguments are not semantically valid. But, allowed in runtime.
4192- # So, we need to warn users about possible invalid usage.
4193- if len (call .args ) > 1 :
4194- self .fail ("Only the first argument to ParamSpec has defined semantics" , s )
4223+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4224+ if n_values != 0 :
4225+ self .fail ("Only the first positional argument to ParamSpec has defined semantics" , s )
41954226
41964227 default : Type = AnyType (TypeOfAny .from_omitted_generics )
4228+ for param_value , param_name in zip (
4229+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4230+ ):
4231+ if param_name == "default" :
4232+ tv_arg = self .get_typevarlike_argument (
4233+ "ParamSpec" ,
4234+ param_name ,
4235+ param_value ,
4236+ s ,
4237+ allow_unbound_tvars = True ,
4238+ allow_param_spec_literals = True ,
4239+ )
4240+ if tv_arg is None :
4241+ return False
4242+ default = tv_arg
4243+ if isinstance (tv_arg , Parameters ):
4244+ for i , arg_type in enumerate (tv_arg .arg_types ):
4245+ typ = get_proper_type (arg_type )
4246+ if isinstance (typ , AnyType ) and typ .is_from_error :
4247+ self .fail (
4248+ f"Argument { i } of ParamSpec default must be a type" , param_value
4249+ )
4250+ elif not isinstance (default , (AnyType , UnboundType )):
4251+ self .fail (
4252+ "The default argument to ParamSpec must be a tuple expression, ellipsis, or a ParamSpec" ,
4253+ param_value ,
4254+ )
4255+ default = AnyType (TypeOfAny .from_error )
4256+ else :
4257+ # ParamSpec is different from a regular TypeVar:
4258+ # arguments are not semantically valid. But, allowed in runtime.
4259+ # So, we need to warn users about possible invalid usage.
4260+ self .fail (
4261+ "The variance and bound arguments to ParamSpec do not have defined semantics yet" ,
4262+ s ,
4263+ )
41974264
41984265 # PEP 612 reserves the right to define bound, covariant and contravariant arguments to
41994266 # ParamSpec in a later PEP. If and when that happens, we should do something
@@ -4227,10 +4294,34 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool:
42274294 if not call :
42284295 return False
42294296
4230- if len (call .args ) > 1 :
4231- self .fail ("Only the first argument to TypeVarTuple has defined semantics" , s )
4297+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4298+ if n_values != 0 :
4299+ self .fail (
4300+ "Only the first positional argument to TypeVarTuple has defined semantics" , s
4301+ )
42324302
42334303 default : Type = AnyType (TypeOfAny .from_omitted_generics )
4304+ for param_value , param_name in zip (
4305+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4306+ ):
4307+ if param_name == "default" :
4308+ tv_arg = self .get_typevarlike_argument (
4309+ "TypeVarTuple" , param_name , param_value , s , allow_unbound_tvars = True
4310+ )
4311+ if tv_arg is None :
4312+ return False
4313+ default = tv_arg
4314+ if not isinstance (default , UnpackType ):
4315+ self .fail (
4316+ "The default argument to TypeVarTuple must be an Unpacked tuple" ,
4317+ param_value ,
4318+ )
4319+ default = AnyType (TypeOfAny .from_error )
4320+ else :
4321+ self .fail (
4322+ "The variance and bound arguments to TypeVarTuple do not have defined semantics yet" ,
4323+ s ,
4324+ )
42344325
42354326 if not self .incomplete_feature_enabled (TYPE_VAR_TUPLE , s ):
42364327 return False
@@ -6324,6 +6415,8 @@ def expr_to_analyzed_type(
63246415 report_invalid_types : bool = True ,
63256416 allow_placeholder : bool = False ,
63266417 allow_type_any : bool = False ,
6418+ allow_unbound_tvars : bool = False ,
6419+ allow_param_spec_literals : bool = False ,
63276420 ) -> Type | None :
63286421 if isinstance (expr , CallExpr ):
63296422 # This is a legacy syntax intended mostly for Python 2, we keep it for
@@ -6352,6 +6445,8 @@ def expr_to_analyzed_type(
63526445 report_invalid_types = report_invalid_types ,
63536446 allow_placeholder = allow_placeholder ,
63546447 allow_type_any = allow_type_any ,
6448+ allow_unbound_tvars = allow_unbound_tvars ,
6449+ allow_param_spec_literals = allow_param_spec_literals ,
63556450 )
63566451
63576452 def analyze_type_expr (self , expr : Expression ) -> None :
0 commit comments