@@ -4135,28 +4135,15 @@ def process_typevar_parameters(
4135
4135
if has_values :
4136
4136
self .fail ("TypeVar cannot have both values and an upper bound" , context )
4137
4137
return None
4138
- try :
4139
- # We want to use our custom error message below, so we suppress
4140
- # the default error message for invalid types here.
4141
- analyzed = self .expr_to_analyzed_type (
4142
- param_value , allow_placeholder = True , report_invalid_types = False
4143
- )
4144
- if analyzed is None :
4145
- # Type variables are special: we need to place them in the symbol table
4146
- # soon, even if upper bound is not ready yet. Otherwise avoiding
4147
- # a "deadlock" in this common pattern would be tricky:
4148
- # T = TypeVar('T', bound=Custom[Any])
4149
- # class Custom(Generic[T]):
4150
- # ...
4151
- analyzed = PlaceholderType (None , [], context .line )
4152
- upper_bound = get_proper_type (analyzed )
4153
- if isinstance (upper_bound , AnyType ) and upper_bound .is_from_error :
4154
- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4155
- # Note: we do not return 'None' here -- we want to continue
4156
- # using the AnyType as the upper bound.
4157
- except TypeTranslationError :
4158
- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4138
+ tv_arg = self .get_typevarlike_argument ("TypeVar" , param_name , param_value , context )
4139
+ if tv_arg is None :
4159
4140
return None
4141
+ upper_bound = tv_arg
4142
+ elif param_name == "default" :
4143
+ tv_arg = self .get_typevarlike_argument (
4144
+ "TypeVar" , param_name , param_value , context , allow_unbound_tvars = True
4145
+ )
4146
+ default = tv_arg or AnyType (TypeOfAny .from_error )
4160
4147
elif param_name == "values" :
4161
4148
# Probably using obsolete syntax with values=(...). Explain the current syntax.
4162
4149
self .fail ('TypeVar "values" argument not supported' , context )
@@ -4184,6 +4171,52 @@ def process_typevar_parameters(
4184
4171
variance = INVARIANT
4185
4172
return variance , upper_bound , default
4186
4173
4174
+ def get_typevarlike_argument (
4175
+ self ,
4176
+ typevarlike_name : str ,
4177
+ param_name : str ,
4178
+ param_value : Expression ,
4179
+ context : Context ,
4180
+ * ,
4181
+ allow_unbound_tvars : bool = False ,
4182
+ allow_param_spec_literals : bool = False ,
4183
+ report_invalid_typevar_arg : bool = True ,
4184
+ ) -> ProperType | None :
4185
+ try :
4186
+ # We want to use our custom error message below, so we suppress
4187
+ # the default error message for invalid types here.
4188
+ analyzed = self .expr_to_analyzed_type (
4189
+ param_value ,
4190
+ allow_placeholder = True ,
4191
+ report_invalid_types = False ,
4192
+ allow_unbound_tvars = allow_unbound_tvars ,
4193
+ allow_param_spec_literals = allow_param_spec_literals ,
4194
+ )
4195
+ if analyzed is None :
4196
+ # Type variables are special: we need to place them in the symbol table
4197
+ # soon, even if upper bound is not ready yet. Otherwise avoiding
4198
+ # a "deadlock" in this common pattern would be tricky:
4199
+ # T = TypeVar('T', bound=Custom[Any])
4200
+ # class Custom(Generic[T]):
4201
+ # ...
4202
+ analyzed = PlaceholderType (None , [], context .line )
4203
+ typ = get_proper_type (analyzed )
4204
+ if report_invalid_typevar_arg and isinstance (typ , AnyType ) and typ .is_from_error :
4205
+ self .fail (
4206
+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4207
+ param_value ,
4208
+ )
4209
+ # Note: we do not return 'None' here -- we want to continue
4210
+ # using the AnyType.
4211
+ return typ
4212
+ except TypeTranslationError :
4213
+ if report_invalid_typevar_arg :
4214
+ self .fail (
4215
+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4216
+ param_value ,
4217
+ )
4218
+ return None
4219
+
4187
4220
def extract_typevarlike_name (self , s : AssignmentStmt , call : CallExpr ) -> str | None :
4188
4221
if not call :
4189
4222
return None
@@ -4216,13 +4249,50 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool:
4216
4249
if name is None :
4217
4250
return False
4218
4251
4219
- # ParamSpec is different from a regular TypeVar:
4220
- # arguments are not semantically valid. But, allowed in runtime.
4221
- # So, we need to warn users about possible invalid usage.
4222
- if len (call .args ) > 1 :
4223
- self .fail ("Only the first argument to ParamSpec has defined semantics" , s )
4252
+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4253
+ if n_values != 0 :
4254
+ self .fail ('Too many positional arguments for "ParamSpec"' , s )
4224
4255
4225
4256
default : Type = AnyType (TypeOfAny .from_omitted_generics )
4257
+ for param_value , param_name in zip (
4258
+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4259
+ ):
4260
+ if param_name == "default" :
4261
+ tv_arg = self .get_typevarlike_argument (
4262
+ "ParamSpec" ,
4263
+ param_name ,
4264
+ param_value ,
4265
+ s ,
4266
+ allow_unbound_tvars = True ,
4267
+ allow_param_spec_literals = True ,
4268
+ report_invalid_typevar_arg = False ,
4269
+ )
4270
+ default = tv_arg or AnyType (TypeOfAny .from_error )
4271
+ if isinstance (tv_arg , Parameters ):
4272
+ for i , arg_type in enumerate (tv_arg .arg_types ):
4273
+ typ = get_proper_type (arg_type )
4274
+ if isinstance (typ , AnyType ) and typ .is_from_error :
4275
+ self .fail (
4276
+ f"Argument { i } of ParamSpec default must be a type" , param_value
4277
+ )
4278
+ elif (
4279
+ isinstance (default , AnyType )
4280
+ and default .is_from_error
4281
+ or not isinstance (default , (AnyType , UnboundType ))
4282
+ ):
4283
+ self .fail (
4284
+ "The default argument to ParamSpec must be a list expression, ellipsis, or a ParamSpec" ,
4285
+ param_value ,
4286
+ )
4287
+ default = AnyType (TypeOfAny .from_error )
4288
+ else :
4289
+ # ParamSpec is different from a regular TypeVar:
4290
+ # arguments are not semantically valid. But, allowed in runtime.
4291
+ # So, we need to warn users about possible invalid usage.
4292
+ self .fail (
4293
+ "The variance and bound arguments to ParamSpec do not have defined semantics yet" ,
4294
+ s ,
4295
+ )
4226
4296
4227
4297
# PEP 612 reserves the right to define bound, covariant and contravariant arguments to
4228
4298
# ParamSpec in a later PEP. If and when that happens, we should do something
@@ -4256,10 +4326,32 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool:
4256
4326
if not call :
4257
4327
return False
4258
4328
4259
- if len (call .args ) > 1 :
4260
- self .fail ("Only the first argument to TypeVarTuple has defined semantics" , s )
4329
+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4330
+ if n_values != 0 :
4331
+ self .fail ('Too many positional arguments for "TypeVarTuple"' , s )
4261
4332
4262
4333
default : Type = AnyType (TypeOfAny .from_omitted_generics )
4334
+ for param_value , param_name in zip (
4335
+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4336
+ ):
4337
+ if param_name == "default" :
4338
+ tv_arg = self .get_typevarlike_argument (
4339
+ "TypeVarTuple" ,
4340
+ param_name ,
4341
+ param_value ,
4342
+ s ,
4343
+ allow_unbound_tvars = True ,
4344
+ report_invalid_typevar_arg = False ,
4345
+ )
4346
+ default = tv_arg or AnyType (TypeOfAny .from_error )
4347
+ if not isinstance (default , UnpackType ):
4348
+ self .fail (
4349
+ "The default argument to TypeVarTuple must be an Unpacked tuple" ,
4350
+ param_value ,
4351
+ )
4352
+ default = AnyType (TypeOfAny .from_error )
4353
+ else :
4354
+ self .fail (f'Unexpected keyword argument "{ param_name } " for "TypeVarTuple"' , s )
4263
4355
4264
4356
if not self .incomplete_feature_enabled (TYPE_VAR_TUPLE , s ):
4265
4357
return False
@@ -6359,6 +6451,8 @@ def expr_to_analyzed_type(
6359
6451
report_invalid_types : bool = True ,
6360
6452
allow_placeholder : bool = False ,
6361
6453
allow_type_any : bool = False ,
6454
+ allow_unbound_tvars : bool = False ,
6455
+ allow_param_spec_literals : bool = False ,
6362
6456
) -> Type | None :
6363
6457
if isinstance (expr , CallExpr ):
6364
6458
# This is a legacy syntax intended mostly for Python 2, we keep it for
@@ -6387,6 +6481,8 @@ def expr_to_analyzed_type(
6387
6481
report_invalid_types = report_invalid_types ,
6388
6482
allow_placeholder = allow_placeholder ,
6389
6483
allow_type_any = allow_type_any ,
6484
+ allow_unbound_tvars = allow_unbound_tvars ,
6485
+ allow_param_spec_literals = allow_param_spec_literals ,
6390
6486
)
6391
6487
6392
6488
def analyze_type_expr (self , expr : Expression ) -> None :
0 commit comments