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