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