@@ -3705,6 +3705,10 @@ def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: ...
37053705 self .assertEqual (Y .__parameters__ , ())
37063706 self .assertEqual (Y .__args__ , ((int , str , str ), bytes , memoryview ))
37073707
3708+ # Regression test; fixing #126 might cause an error here
3709+ with self .assertRaisesRegex (TypeError , "not a generic class" ):
3710+ Y [int ]
3711+
37083712 def test_protocol_generic_over_typevartuple (self ):
37093713 Ts = TypeVarTuple ("Ts" )
37103714 T = TypeVar ("T" )
@@ -5259,6 +5263,7 @@ class X(Generic[T, P]):
52595263 class Y (Protocol [T , P ]):
52605264 pass
52615265
5266+ things = "arguments" if sys .version_info >= (3 , 10 ) else "parameters"
52625267 for klass in X , Y :
52635268 with self .subTest (klass = klass .__name__ ):
52645269 G1 = klass [int , P_2 ]
@@ -5273,20 +5278,146 @@ class Y(Protocol[T, P]):
52735278 self .assertEqual (G3 .__args__ , (int , Concatenate [int , ...]))
52745279 self .assertEqual (G3 .__parameters__ , ())
52755280
5281+ with self .assertRaisesRegex (
5282+ TypeError ,
5283+ f"Too few { things } for { klass } "
5284+ ):
5285+ klass [int ]
5286+
52765287 # The following are some valid uses cases in PEP 612 that don't work:
52775288 # These do not work in 3.9, _type_check blocks the list and ellipsis.
52785289 # G3 = X[int, [int, bool]]
52795290 # G4 = X[int, ...]
52805291 # 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" )
52835344
52845345 class Z (Generic [P ]):
52855346 pass
52865347
52875348 class ProtoZ (Protocol [P ]):
52885349 pass
52895350
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+
52905421 def test_pickle (self ):
52915422 global P , P_co , P_contra , P_default
52925423 P = ParamSpec ('P' )
@@ -5468,6 +5599,43 @@ def test_eq(self):
54685599 self .assertEqual (hash (C4 ), hash (C5 ))
54695600 self .assertNotEqual (C4 , C6 )
54705601
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 ))
54715639
54725640class TypeGuardTests (BaseTestCase ):
54735641 def test_basics (self ):
@@ -7465,11 +7633,9 @@ def test_callable_with_concatenate(self):
74657633 self .assertEqual (callable_concat .__parameters__ , (P2 ,))
74667634 concat_usage = callable_concat [str ]
74677635 with self .subTest ("get_args of Concatenate in TypeAliasType" ):
7468- if not TYPING_3_9_0 :
7636+ if not TYPING_3_10_0 :
74697637 # args are: ([<class 'int'>, ~P2],)
74707638 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" )
74737639 self .assertEqual (get_args (concat_usage ), ((int , str ),))
74747640 with self .subTest ("Equality of parameter_expression without []" ):
74757641 if not TYPING_3_10_0 :
0 commit comments