@@ -3705,6 +3705,10 @@ def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: ...
3705
3705
self .assertEqual (Y .__parameters__ , ())
3706
3706
self .assertEqual (Y .__args__ , ((int , str , str ), bytes , memoryview ))
3707
3707
3708
+ # Regression test; fixing #126 might cause an error here
3709
+ with self .assertRaisesRegex (TypeError , "not a generic class" ):
3710
+ Y [int ]
3711
+
3708
3712
def test_protocol_generic_over_typevartuple (self ):
3709
3713
Ts = TypeVarTuple ("Ts" )
3710
3714
T = TypeVar ("T" )
@@ -5259,6 +5263,7 @@ class X(Generic[T, P]):
5259
5263
class Y (Protocol [T , P ]):
5260
5264
pass
5261
5265
5266
+ things = "arguments" if sys .version_info >= (3 , 10 ) else "parameters"
5262
5267
for klass in X , Y :
5263
5268
with self .subTest (klass = klass .__name__ ):
5264
5269
G1 = klass [int , P_2 ]
@@ -5273,20 +5278,146 @@ class Y(Protocol[T, P]):
5273
5278
self .assertEqual (G3 .__args__ , (int , Concatenate [int , ...]))
5274
5279
self .assertEqual (G3 .__parameters__ , ())
5275
5280
5281
+ with self .assertRaisesRegex (
5282
+ TypeError ,
5283
+ f"Too few { things } for { klass } "
5284
+ ):
5285
+ klass [int ]
5286
+
5276
5287
# The following are some valid uses cases in PEP 612 that don't work:
5277
5288
# These do not work in 3.9, _type_check blocks the list and ellipsis.
5278
5289
# G3 = X[int, [int, bool]]
5279
5290
# G4 = X[int, ...]
5280
5291
# G5 = Z[[int, str, bool]]
5281
- # Not working because this is special-cased in 3.10.
5282
- # G6 = Z[int, str, bool]
5292
+
5293
+ def test_single_argument_generic (self ):
5294
+ P = ParamSpec ("P" )
5295
+ T = TypeVar ("T" )
5296
+ P_2 = ParamSpec ("P_2" )
5297
+
5298
+ class Z (Generic [P ]):
5299
+ pass
5300
+
5301
+ class ProtoZ (Protocol [P ]):
5302
+ pass
5303
+
5304
+ for klass in Z , ProtoZ :
5305
+ with self .subTest (klass = klass .__name__ ):
5306
+ # Note: For 3.10+ __args__ are nested tuples here ((int, ),) instead of (int, )
5307
+ G6 = klass [int , str , T ]
5308
+ G6args = G6 .__args__ [0 ] if sys .version_info >= (3 , 10 ) else G6 .__args__
5309
+ self .assertEqual (G6args , (int , str , T ))
5310
+ self .assertEqual (G6 .__parameters__ , (T ,))
5311
+
5312
+ # P = [int]
5313
+ G7 = klass [int ]
5314
+ G7args = G7 .__args__ [0 ] if sys .version_info >= (3 , 10 ) else G7 .__args__
5315
+ self .assertEqual (G7args , (int ,))
5316
+ self .assertEqual (G7 .__parameters__ , ())
5317
+
5318
+ G8 = klass [Concatenate [T , ...]]
5319
+ self .assertEqual (G8 .__args__ , (Concatenate [T , ...], ))
5320
+ self .assertEqual (G8 .__parameters__ , (T ,))
5321
+
5322
+ G9 = klass [Concatenate [T , P_2 ]]
5323
+ self .assertEqual (G9 .__args__ , (Concatenate [T , P_2 ], ))
5324
+
5325
+ # This is an invalid form but useful for testing correct subsitution
5326
+ G10 = klass [int , Concatenate [str , P ]]
5327
+ G10args = G10 .__args__ [0 ] if sys .version_info >= (3 , 10 ) else G10 .__args__
5328
+ self .assertEqual (G10args , (int , Concatenate [str , P ], ))
5329
+
5330
+ @skipUnless (TYPING_3_10_0 , "ParamSpec not present before 3.10" )
5331
+ def test_is_param_expr (self ):
5332
+ P = ParamSpec ("P" )
5333
+ P_typing = typing .ParamSpec ("P_typing" )
5334
+ self .assertTrue (typing_extensions ._is_param_expr (P ))
5335
+ self .assertTrue (typing_extensions ._is_param_expr (P_typing ))
5336
+ if hasattr (typing , "_is_param_expr" ):
5337
+ self .assertTrue (typing ._is_param_expr (P ))
5338
+ self .assertTrue (typing ._is_param_expr (P_typing ))
5339
+
5340
+ def test_single_argument_generic_with_parameter_expressions (self ):
5341
+ P = ParamSpec ("P" )
5342
+ T = TypeVar ("T" )
5343
+ P_2 = ParamSpec ("P_2" )
5283
5344
5284
5345
class Z (Generic [P ]):
5285
5346
pass
5286
5347
5287
5348
class ProtoZ (Protocol [P ]):
5288
5349
pass
5289
5350
5351
+ things = "arguments" if sys .version_info >= (3 , 10 ) else "parameters"
5352
+ for klass in Z , ProtoZ :
5353
+ with self .subTest (klass = klass .__name__ ):
5354
+ G8 = klass [Concatenate [T , ...]]
5355
+
5356
+ H8_1 = G8 [int ]
5357
+ self .assertEqual (H8_1 .__parameters__ , ())
5358
+ with self .assertRaisesRegex (TypeError , "not a generic class" ):
5359
+ H8_1 [str ]
5360
+
5361
+ H8_2 = G8 [T ][int ]
5362
+ self .assertEqual (H8_2 .__parameters__ , ())
5363
+ with self .assertRaisesRegex (TypeError , "not a generic class" ):
5364
+ H8_2 [str ]
5365
+
5366
+ G9 = klass [Concatenate [T , P_2 ]]
5367
+ self .assertEqual (G9 .__parameters__ , (T , P_2 ))
5368
+
5369
+ with self .assertRaisesRegex (TypeError ,
5370
+ "The last parameter to Concatenate should be a ParamSpec variable or ellipsis."
5371
+ if sys .version_info < (3 , 10 ) else
5372
+ # from __typing_subst__
5373
+ "Expected a list of types, an ellipsis, ParamSpec, or Concatenate"
5374
+ ):
5375
+ G9 [int , int ]
5376
+
5377
+ with self .assertRaisesRegex (TypeError , f"Too few { things } " ):
5378
+ G9 [int ]
5379
+
5380
+ with self .subTest ("Check list as parameter expression" , klass = klass .__name__ ):
5381
+ if sys .version_info < (3 , 10 ):
5382
+ self .skipTest ("Cannot pass non-types" )
5383
+ G5 = klass [[int , str , T ]]
5384
+ self .assertEqual (G5 .__parameters__ , (T ,))
5385
+ self .assertEqual (G5 .__args__ , ((int , str , T ),))
5386
+
5387
+ H9 = G9 [int , [T ]]
5388
+ self .assertEqual (H9 .__parameters__ , (T ,))
5389
+
5390
+ # This is an invalid parameter expression but useful for testing correct subsitution
5391
+ G10 = klass [int , Concatenate [str , P ]]
5392
+ with self .subTest ("Check invalid form substitution" ):
5393
+ self .assertEqual (G10 .__parameters__ , (P , ))
5394
+ if sys .version_info < (3 , 9 ):
5395
+ self .skipTest ("3.8 typing._type_subst does not support this substitution process" )
5396
+ H10 = G10 [int ]
5397
+ if (3 , 10 ) <= sys .version_info < (3 , 11 , 3 ):
5398
+ self .skipTest ("3.10-3.11.2 does not substitute Concatenate here" )
5399
+ self .assertEqual (H10 .__parameters__ , ())
5400
+ H10args = H10 .__args__ [0 ] if sys .version_info >= (3 , 10 ) else H10 .__args__
5401
+ self .assertEqual (H10args , (int , (str , int )))
5402
+
5403
+ @skipUnless (TYPING_3_10_0 , "ParamSpec not present before 3.10" )
5404
+ def test_substitution_with_typing_variants (self ):
5405
+ # verifies substitution and typing._check_generic working with typing variants
5406
+ P = ParamSpec ("P" )
5407
+ typing_P = typing .ParamSpec ("typing_P" )
5408
+ typing_Concatenate = typing .Concatenate [int , P ]
5409
+
5410
+ class Z (Generic [typing_P ]):
5411
+ pass
5412
+
5413
+ P1 = Z [typing_P ]
5414
+ self .assertEqual (P1 .__parameters__ , (typing_P ,))
5415
+ self .assertEqual (P1 .__args__ , (typing_P ,))
5416
+
5417
+ C1 = Z [typing_Concatenate ]
5418
+ self .assertEqual (C1 .__parameters__ , (P ,))
5419
+ self .assertEqual (C1 .__args__ , (typing_Concatenate ,))
5420
+
5290
5421
def test_pickle (self ):
5291
5422
global P , P_co , P_contra , P_default
5292
5423
P = ParamSpec ('P' )
<
F987
/div>
@@ -5468,6 +5599,43 @@ def test_eq(self):
5468
5599
self .assertEqual (hash (C4 ), hash (C5 ))
5469
5600
self .assertNotEqual (C4 , C6 )
5470
5601
5602
+ def test_substitution (self ):
5603
+ T = TypeVar ('T' )
5604
+ P = ParamSpec ('P' )
5605
+ Ts = TypeVarTuple ("Ts" )
5606
+
5607
+ C1 = Concatenate [str , T , ...]
5608
+ self .assertEqual (C1 [int ], Concatenate [str , int , ...])
5609
+
5610
+ C2 = Concatenate [str , P ]
5611
+ self .assertEqual (C2 [...], Concatenate [str , ...])
5612
+ self .assertEqual (C2 [int ], (str , int ))
5613
+ U1 = Unpack [Tuple [int , str ]]
5614
+ U2 = Unpack [Ts ]
5615
+ self .assertEqual (C2 [U1 ], (str , int , str ))
5616
+ self .assertEqual (C2 [U2 ], (str , Unpack [Ts ]))
5617
+ self .assertEqual (C2 ["U2" ], (str , typing .ForwardRef ("U2" )))
5618
+
5619
+ if (3 , 12 , 0 ) <= sys .version_info < (3 , 12 , 4 ):
5620
+ with self .assertRaises (AssertionError ):
5621
+ C2 [Unpack [U2 ]]
5622
+ else :
5623
+ with self .assertRaisesRegex (TypeError , "must be used with a tuple type" ):
5624
+ C2 [Unpack [U2 ]]
5625
+
5626
+ C3 = Concatenate [str , T , P ]
5627
+ self .assertEqual (C3 [int , [bool ]], (str , int , bool ))
5628
+
5629
+ @skipUnless (TYPING_3_10_0 , "Concatenate not present before 3.10" )
5630
+ def test_is_param_expr (self ):
5631
+ P = ParamSpec ('P' )
5632
+ concat = Concatenate [str , P ]
5633
+ typing_concat = typing .Concatenate [str , P ]
5634
+ self .assertTrue (typing_extensions ._is_param_expr (concat ))
5635
+ self .assertTrue (typing_extensions ._is_param_expr (typing_concat ))
5636
+ if hasattr (typing , "_is_param_expr" ):
5637
+ self .assertTrue (typing ._is_param_expr (concat ))
5638
+ self .assertTrue (typing ._is_param_expr (typing_concat ))
5471
5639
5472
5640
class TypeGuardTests (BaseTestCase ):
5473
5641
def test_basics (self ):
@@ -7465,11 +7633,9 @@ def test_callable_with_concatenate(self):
7465
7633
self .assertEqual (callable_concat .__parameters__ , (P2 ,))
7466
7634
concat_usage = callable_concat [str ]
7467
7635
with self .subTest ("get_args of Concatenate in TypeAliasType" ):
7468
- if not TYPING_3_9_0 :
7636
+ if not TYPING_3_10_0 :
7469
7637
# args are: ([<class 'int'>, ~P2],)
7470
7638
self .skipTest ("Nested ParamSpec is not substituted" )
7471
- if sys .version_info < (3 , 10 , 2 ):
7472
- self .skipTest ("GenericAlias keeps Concatenate in __args__ prior to 3.10.2" )
7473
7639
self .assertEqual (get_args (concat_usage ), ((int , str ),))
7474
7640
with self .subTest ("Equality of parameter_expression without []" ):
7475
7641
if not TYPING_3_10_0 :
0 commit comments