@@ -4106,28 +4106,17 @@ def process_typevar_parameters(
4106
4106
if has_values :
4107
4107
self .fail ("TypeVar cannot have both values and an upper bound" , context )
4108
4108
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 :
4130
4111
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
4131
4120
elif param_name == "values" :
4132
4121
# Probably using obsolete syntax with values=(...). Explain the current syntax.
4133
4122
self .fail ('TypeVar "values" argument not supported' , context )
@@ -4155,6 +4144,50 @@ def process_typevar_parameters(
4155
4144
variance = INVARIANT
4156
4145
return variance , upper_bound , default
4157
4146
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
+
4158
4191
def extract_typevarlike_name (self , s : AssignmentStmt , call : CallExpr ) -> str | None :
4159
4192
if not call :
4160
4193
return None
@@ -4187,13 +4220,47 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool:
4187
4220
if name is None :
4188
4221
return False
4189
4222
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 )
4195
4226
4196
4227
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
+ )
4197
4264
4198
4265
# PEP 612 reserves the right to define bound, covariant and contravariant arguments to
4199
4266
# 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:
4227
4294
if not call :
4228
4295
return False
4229
4296
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
+ )
4232
4302
4233
4303
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
+ )
4234
4325
4235
4326
if not self .incomplete_feature_enabled (TYPE_VAR_TUPLE , s ):
4236
4327
return False
@@ -6324,6 +6415,8 @@ def expr_to_analyzed_type(
6324
6415
report_invalid_types : bool = True ,
6325
6416
allow_placeholder : bool = False ,
6326
6417
allow_type_any : bool = False ,
6418
+ allow_unbound_tvars : bool = False ,
6419
+ allow_param_spec_literals : bool = False ,
6327
6420
) -> Type | None :
6328
6421
if isinstance (expr , CallExpr ):
6329
6422
# This is a legacy syntax intended mostly for Python 2, we keep it for
@@ -6352,6 +6445,8 @@ def expr_to_analyzed_type(
6352
6445
report_invalid_types = report_invalid_types ,
6353
6446
allow_placeholder = allow_placeholder ,
6354
6447
allow_type_any = allow_type_any ,
6448
+ allow_unbound_tvars = allow_unbound_tvars ,
6449
+ allow_param_spec_literals = allow_param_spec_literals ,
6355
6450
)
6356
6451
6357
6452
def analyze_type_expr (self , expr : Expression ) -> None :
0 commit comments