@@ -4124,28 +4124,17 @@ def process_typevar_parameters(
4124
4124
if has_values :
4125
4125
self .fail ("TypeVar cannot have both values and an upper bound" , context )
4126
4126
return None
4127
- try :
4128
- # We want to use our custom error message below, so we suppress
4129
- # the default error message for invalid types here.
4130
- analyzed = self .expr_to_analyzed_type (
4131
- param_value , allow_placeholder = True , report_invalid_types = False
4132
- )
4133
- if analyzed is None :
4134
- # Type variables are special: we need to place them in the symbol table
4135
- # soon, even if upper bound is not ready yet. Otherwise avoiding
4136
- # a "deadlock" in this common pattern would be tricky:
4137
- # T = TypeVar('T', bound=Custom[Any])
4138
- # class Custom(Generic[T]):
4139
- # ...
4140
- analyzed = PlaceholderType (None , [], context .line )
4141
- upper_bound = get_proper_type (analyzed )
4142
- if isinstance (upper_bound , AnyType ) and upper_bound .is_from_error :
4143
- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4144
- # Note: we do not return 'None' here -- we want to continue
4145
- # using the AnyType as the upper bound.
4146
- except TypeTranslationError :
4147
- self .fail (message_registry .TYPEVAR_BOUND_MUST_BE_TYPE , param_value )
4127
+ tv_arg = self .get_typevarlike_argument ("TypeVar" , param_name , param_value , context )
4128
+ if tv_arg is None :
4148
4129
return None
4130
+ upper_bound = tv_arg
4131
+ elif param_name == "default" :
4132
+ tv_arg = self .get_typevarlike_argument (
4133
+ "TypeVar" , param_name , param_value , context , allow_unbound_tvars = True
4134
+ )
4135
+ if tv_arg is None :
4136
+ return None
4137
+ default = tv_arg
4149
4138
elif param_name == "values" :
4150
4139
# Probably using obsolete syntax with values=(...). Explain the current syntax.
4151
4140
self .fail ('TypeVar "values" argument not supported' , context )
@@ -4173,6 +4162,50 @@ def process_typevar_parameters(
4173
4162
variance = INVARIANT
4174
4163
return variance , upper_bound , default
4175
4164
4165
+ def get_typevarlike_argument (
4166
+ self ,
4167
+ typevarlike_name : str ,
4168
+ param_name : str ,
4169
+ param_value : Expression ,
4170
+ context : Context ,
4171
+ * ,
4172
+ allow_unbound_tvars : bool = False ,
4173
+ allow_param_spec_literals : bool = False ,
4174
+ ) -> ProperType | None :
4175
+ try :
4176
+ # We want to use our custom error message below, so we suppress
4177
+ # the default error message for invalid types here.
4178
+ analyzed = self .expr_to_analyzed_type (
4179
+ param_value ,
4180
+ allow_placeholder = True ,
4181
+ report_invalid_types = False ,
4182
+ allow_unbound_tvars = allow_unbound_tvars ,
4183
+ allow_param_spec_literals = allow_param_spec_literals ,
4184
+ )
4185
+ if analyzed is None :
4186
+ # Type variables are special: we need to place them in the symbol table
4187
+ # soon, even if upper bound is not ready yet. Otherwise avoiding
4188
+ # a "deadlock" in this common pattern would be tricky:
4189
+ # T = TypeVar('T', bound=Custom[Any])
4190
+ # class Custom(Generic[T]):
4191
+ # ...
4192
+ analyzed = PlaceholderType (None , [], context .line )
4193
+ typ = get_proper_type (analyzed )
4194
+ if isinstance (typ , AnyType ) and typ .is_from_error :
4195
+ self .fail (
4196
+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4197
+ param_value ,
4198
+ )
4199
+ # Note: we do not return 'None' here -- we want to continue
4200
+ # using the AnyType as the upper bound.
4201
+ return typ
4202
+ except TypeTranslationError :
4203
+ self .fail (
4204
+ message_registry .TYPEVAR_ARG_MUST_BE_TYPE .format (typevarlike_name , param_name ),
4205
+ param_value ,
4206
+ )
4207
+ return None
4208
+
4176
4209
def extract_typevarlike_name (self , s : AssignmentStmt , call : CallExpr ) -> str | None :
4177
4210
if not call :
4178
4211
return None
@@ -4205,13 +4238,47 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool:
4205
4238
if name is None :
4206
4239
return False
4207
4240
4208
- # ParamSpec is different from a regular TypeVar:
4209
- # arguments are not semantically valid. But, allowed in runtime.
4210
- # So, we need to warn users about possible invalid usage.
4211
- if len (call .args ) > 1 :
4212
- self .fail ("Only the first argument to ParamSpec has defined semantics" , s )
4241
+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4242
+ if n_values != 0 :
4243
+ self .fail ("Only the first positional argument to ParamSpec has defined semantics" , s )
4213
4244
4214
4245
default : Type = AnyType (TypeOfAny .from_omitted_generics )
4246
+ for param_value , param_name in zip (
4247
+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4248
+ ):
4249
+ if param_name == "default" :
4250
+ tv_arg = self .get_typevarlike_argument (
4251
+ "ParamSpec" ,
4252
+ param_name ,
4253
+ param_value ,
4254
+ s ,
4255
+ allow_unbound_tvars = True ,
4256
+ allow_param_spec_literals = True ,
4257
+ )
4258
+ if tv_arg is None :
4259
+ return False
4260
+ default = tv_arg
4261
+ if isinstance (tv_arg , Parameters ):
4262
+ for i , arg_type in enumerate (tv_arg .arg_types ):
4263
+ typ = get_proper_type (arg_type )
4264
+ if isinstance (typ , AnyType ) and typ .is_from_error :
4265
+ self .fail (
4266
+ f"Argument { i } of ParamSpec default must be a type" , param_value
4267
+ )
4268
+ elif not isinstance (default , (AnyType , UnboundType )):
4269
+ self .fail (
4270
+ "The default argument to ParamSpec must be a tuple expression, ellipsis, or a ParamSpec" ,
4271
+ param_value ,
4272
+ )
4273
+ default = AnyType (TypeOfAny .from_error )
4274
+ else :
4275
+ # ParamSpec is different from a regular TypeVar:
4276
+ # arguments are not semantically valid. But, allowed in runtime.
4277
+ # So, we need to warn users about possible invalid usage.
4278
+ self .fail (
4279
+ "The variance and bound arguments to ParamSpec do not have defined semantics yet" ,
4280
+ s ,
4281
+ )
4215
4282
4216
4283
# PEP 612 reserves the right to define bound, covariant and contravariant arguments to
4217
4284
# ParamSpec in a later PEP. If and when that happens, we should do something
@@ -4245,10 +4312,34 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool:
4245
4312
if not call :
4246
4313
return False
4247
4314
4248
- if len (call .args ) > 1 :
4249
- self .fail ("Only the first argument to TypeVarTuple has defined semantics" , s )
4315
+ n_values = call .arg_kinds [1 :].count (ARG_POS )
4316
+ if n_values != 0 :
4317
+ self .fail (
4318
+ "Only the first positional argument to TypeVarTuple has defined semantics" , s
4319
+ )
4250
4320
4251
4321
default : Type = AnyType (TypeOfAny .from_omitted_generics )
4322
+ for param_value , param_name in zip (
4323
+ call .args [1 + n_values :], call .arg_names [1 + n_values :]
4324
+ ):
4325
+ if param_name == "default" :
4326
+ tv_arg = self .get_typevarlike_argument (
4327
+ "TypeVarTuple" , param_name , param_value , s , allow_unbound_tvars = True
4328
+ )
4329
+ if tv_arg is None :
4330
+ return False
4331
+ default = tv_arg
4332
+ if not isinstance (default , UnpackType ):
4333
+ self .fail (
4334
+ "The default argument to TypeVarTuple must be an Unpacked tuple" ,
4335
+ param_value ,
4336
+ )
4337
+ default = AnyType (TypeOfAny .from_error )
4338
+ else :
4339
+ self .fail (
4340
+ "The variance and bound arguments to TypeVarTuple do not have defined semantics yet" ,
4341
+ s ,
4342
+ )
4252
4343
4253
4344
if not self .incomplete_feature_enabled (TYPE_VAR_TUPLE , s ):
4254
4345
return False
@@ -6348,6 +6439,8 @@ def expr_to_analyzed_type(
6348
6439
report_invalid_types : bool = True ,
6349
6440
allow_placeholder : bool = False ,
6350
6441
allow_type_any : bool = False ,
6442
+ allow_unbound_tvars : bool = False ,
6443
+ allow_param_spec_literals : bool = False ,
6351
6444
) -> Type | None :
6352
6445
if isinstance (expr , CallExpr ):
6353
6446
# This is a legacy syntax intended mostly for Python 2, we keep it for
@@ -6376,6 +6469,8 @@ def expr_to_analyzed_type(
6376
6469
report_invalid_types = report_invalid_types ,
6377
6470
allow_placeholder = allow_placeholder ,
6378
6471
allow_type_any = allow_type_any ,
6472
+ allow_unbound_tvars = allow_unbound_tvars ,
6473
+ allow_param_spec_literals = allow_param_spec_literals ,
6379
6474
)
6380
6475
6381
6476
def analyze_type_expr (self , expr : Expression ) -> None :
0 commit comments