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