Skip to content

Commit be4cb90

Browse files
bpo-44794: Merge tests for typing.Callable and collection.abc.Callable (GH-27507)
1 parent 0ad1732 commit be4cb90

File tree

3 files changed

+141
-117
lines changed

3 files changed

+141
-117
lines changed

Lib/_collections_abc.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,7 @@ def __getitem__(self, item):
478478
# then X[int, str] == X[[int, str]].
479479
param_len = len(self.__parameters__)
480480
if param_len == 0:
481-
raise TypeError(f'There are no type or parameter specification'
482-
f'variables left in {self}')
481+
raise TypeError(f'{self} is not a generic class')
483482
if (param_len == 1
484483
and isinstance(item, (tuple, list))
485484
and len(item) > 1) or not isinstance(item, tuple):

Lib/test/test_genericalias.py

Lines changed: 0 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -317,96 +317,6 @@ def __new__(cls, *args, **kwargs):
317317
with self.assertRaises(TypeError):
318318
Bad(list, int, bad=int)
319319

320-
def test_abc_callable(self):
321-
# A separate test is needed for Callable since it uses a subclass of
322-
# GenericAlias.
323-
alias = Callable[[int, str], float]
324-
with self.subTest("Testing subscription"):
325-
self.assertIs(alias.__origin__, Callable)
326-
self.assertEqual(alias.__args__, (int, str, float))
327-
self.assertEqual(alias.__parameters__, ())
328-
329-
with self.subTest("Testing instance checks"):
330-
self.assertIsInstance(alias, GenericAlias)
331-
332-
with self.subTest("Testing weakref"):
333-
self.assertEqual(ref(alias)(), alias)
334-
335-
with self.subTest("Testing pickling"):
336-
s = pickle.dumps(alias)
337-
loaded = pickle.loads(s)
338-
self.assertEqual(alias.__origin__, loaded.__origin__)
339-
self.assertEqual(alias.__args__, loaded.__args__)
340-
self.assertEqual(alias.__parameters__, loaded.__parameters__)
341-
342-
with self.subTest("Testing TypeVar substitution"):
343-
C1 = Callable[[int, T], T]
344-
C2 = Callable[[K, T], V]
345-
C3 = Callable[..., T]
346-
self.assertEqual(C1[str], Callable[[int, str], str])
347-
self.assertEqual(C2[int, float, str], Callable[[int, float], str])
348-
self.assertEqual(C3[int], Callable[..., int])
349-
350-
# multi chaining
351-
C4 = C2[int, V, str]
352-
self.assertEqual(repr(C4).split(".")[-1], "Callable[[int, ~V], str]")
353-
self.assertEqual(repr(C4[dict]).split(".")[-1], "Callable[[int, dict], str]")
354-
self.assertEqual(C4[dict], Callable[[int, dict], str])
355-
356-
# substitute a nested GenericAlias (both typing and the builtin
357-
# version)
358-
C5 = Callable[[typing.List[T], tuple[K, T], V], int]
359-
self.assertEqual(C5[int, str, float],
360-
Callable[[typing.List[int], tuple[str, int], float], int])
361-
362-
with self.subTest("Testing type erasure"):
363-
class C1(Callable):
364-
def __call__(self):
365-
return None
366-
a = C1[[int], T]
367-
self.assertIs(a().__class__, C1)
368-
self.assertEqual(a().__orig_class__, C1[[int], T])
369-
370-
# bpo-42195
371-
with self.subTest("Testing collections.abc.Callable's consistency "
372-
"with typing.Callable"):
373-
c1 = typing.Callable[[int, str], dict]
374-
c2 = Callable[[int, str], dict]
375-
self.assertEqual(c1.__args__, c2.__args__)
376-
self.assertEqual(hash(c1.__args__), hash(c2.__args__))
377-
378-
with self.subTest("Testing ParamSpec uses"):
379-
P = typing.ParamSpec('P')
380-
C1 = Callable[P, T]
381-
# substitution
382-
self.assertEqual(C1[int, str], Callable[[int], str])
383-
self.assertEqual(C1[[int, str], str], Callable[[int, str], str])
384-
self.assertEqual(repr(C1).split(".")[-1], "Callable[~P, ~T]")
385-
self.assertEqual(repr(C1[int, str]).split(".")[-1], "Callable[[int], str]")
386-
387-
C2 = Callable[P, int]
388-
# special case in PEP 612 where
389-
# X[int, str, float] == X[[int, str, float]]
390-
self.assertEqual(C2[int, str, float], C2[[int, str, float]])
391-
self.assertEqual(repr(C2).split(".")[-1], "Callable[~P, int]")
392-
self.assertEqual(repr(C2[int, str]).split(".")[-1], "Callable[[int, str], int]")
393-
394-
with self.subTest("Testing Concatenate uses"):
395-
P = typing.ParamSpec('P')
396-
C1 = Callable[typing.Concatenate[int, P], int]
397-
self.assertEqual(repr(C1), "collections.abc.Callable"
398-
"[typing.Concatenate[int, ~P], int]")
399-
400-
with self.subTest("Testing TypeErrors"):
401-
with self.assertRaisesRegex(TypeError, "variables left in"):
402-
alias[int]
403-
P = typing.ParamSpec('P')
404-
C1 = Callable[P, T]
405-
with self.assertRaisesRegex(TypeError, "many arguments for"):
406-
C1[int, str, str]
407-
with self.assertRaisesRegex(TypeError, "few arguments for"):
408-
C1[int]
409-
410320

411321
if __name__ == "__main__":
412322
unittest.main()

Lib/test/test_typing.py

Lines changed: 140 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,8 @@ def test_basics(self):
406406
issubclass(tuple, Tuple[int, str])
407407

408408
class TP(tuple): ...
409-
self.assertTrue(issubclass(tuple, Tuple))
410-
self.assertTrue(issubclass(TP, Tuple))
409+
self.assertIsSubclass(tuple, Tuple)
410+
self.assertIsSubclass(TP, Tuple)
411411

412412
def test_equality(self):
413413
self.assertEqual(Tuple[int], Tuple[int])
@@ -418,7 +418,7 @@ def test_equality(self):
418418
def test_tuple_subclass(self):
419419
class MyTuple(tuple):
420420
pass
421-
self.assertTrue(issubclass(MyTuple, Tuple))
421+
self.assertIsSubclass(MyTuple, Tuple)
422422

423423
def test_tuple_instance_type_error(self):
424424
with self.assertRaises(TypeError):
@@ -439,23 +439,28 @@ def test_errors(self):
439439
issubclass(42, Tuple[int])
440440

441441

442-
class CallableTests(BaseTestCase):
442+
class BaseCallableTests:
443443

444444
def test_self_subclass(self):
445+
Callable = self.Callable
445446
with self.assertRaises(TypeError):
446-
self.assertTrue(issubclass(type(lambda x: x), Callable[[int], int]))
447-
self.assertTrue(issubclass(type(lambda x: x), Callable))
447+
issubclass(types.FunctionType, Callable[[int], int])
448+
self.assertIsSubclass(types.FunctionType, Callable)
448449

449450
def test_eq_hash(self):
450-
self.assertEqual(Callable[[int], int], Callable[[int], int])
451-
self.assertEqual(len({Callable[[int], int], Callable[[int], int]}), 1)
452-
self.assertNotEqual(Callable[[int], int], Callable[[int], str])
453-
self.assertNotEqual(Callable[[int], int], Callable[[str], int])
454-
self.assertNotEqual(Callable[[int], int], Callable[[int, int], int])
455-
self.assertNotEqual(Callable[[int], int], Callable[[], int])
456-
self.assertNotEqual(Callable[[int], int], Callable)
451+
Callable = self.Callable
452+
C = Callable[[int], int]
453+
self.assertEqual(C, Callable[[int], int])
454+
self.assertEqual(len({C, Callable[[int], int]}), 1)
455+
self.assertNotEqual(C, Callable[[int], str])
456+
self.assertNotEqual(C, Callable[[str], int])
457+
self.assertNotEqual(C, Callable[[int, int], int])
458+
self.assertNotEqual(C, Callable[[], int])
459+
self.assertNotEqual(C, Callable[..., int])
460+
self.assertNotEqual(C, Callable)
457461

458462
def test_cannot_instantiate(self):
463+
Callable = self.Callable
459464
with self.assertRaises(TypeError):
460465
Callable()
461466
with self.assertRaises(TypeError):
@@ -467,16 +472,19 @@ def test_cannot_instantiate(self):
467472
type(c)()
468473

469474
def test_callable_wrong_forms(self):
475+
Callable = self.Callable
470476
with self.assertRaises(TypeError):
471477
Callable[int]
472478

473479
def test_callable_instance_works(self):
480+
Callable = self.Callable
474481
def f():
475482
pass
476483
self.assertIsInstance(f, Callable)
477484
self.assertNotIsInstance(None, Callable)
478485

479486
def test_callable_instance_type_error(self):
487+
Callable = self.Callable
480488
def f():
481489
pass
482490
with self.assertRaises(TypeError):
@@ -489,28 +497,142 @@ def f():
489497
self.assertNotIsInstance(None, Callable[[], Any])
490498

491499
def test_repr(self):
500+
Callable = self.Callable
501+
fullname = f'{Callable.__module__}.Callable'
492502
ct0 = Callable[[], bool]
493-
self.assertEqual(repr(ct0), 'typing.Callable[[], bool]')
503+
self.assertEqual(repr(ct0), f'{fullname}[[], bool]')
494504
ct2 = Callable[[str, float], int]
495-
self.assertEqual(repr(ct2), 'typing.Callable[[str, float], int]')
505+
self.assertEqual(repr(ct2), f'{fullname}[[str, float], int]')
496506
ctv = Callable[..., str]
497-
self.assertEqual(repr(ctv), 'typing.Callable[..., str]')
507+
self.assertEqual(repr(ctv), f'{fullname}[..., str]')
498508
ct3 = Callable[[str, float], list[int]]
499-
self.assertEqual(repr(ct3), 'typing.Callable[[str, float], list[int]]')
509+
self.assertEqual(repr(ct3), f'{fullname}[[str, float], list[int]]')
500510

501511
def test_callable_with_ellipsis(self):
502-
512+
Callable = self.Callable
503513
def foo(a: Callable[..., T]):
504514
pass
505515

506516
self.assertEqual(get_type_hints(foo, globals(), locals()),
507517
{'a': Callable[..., T]})
508518

509519
def test_ellipsis_in_generic(self):
520+
Callable = self.Callable
510521
# Shouldn't crash; see https://github.com/python/typing/issues/259
511522
typing.List[Callable[..., str]]
512523

513524

525+
def test_basic(self):
526+
Callable = self.Callable
527+
alias = Callable[[int, str], float]
528+
if Callable is collections.abc.Callable:
529+
self.assertIsInstance(alias, types.GenericAlias)
530+
self.assertIs(alias.__origin__, collections.abc.Callable)
531+
self.assertEqual(alias.__args__, (int, str, float))
532+
self.assertEqual(alias.__parameters__, ())
533+
534+
def test_weakref(self):
535+
Callable = self.Callable
536+
alias = Callable[[int, str], float]
537+
self.assertEqual(weakref.ref(alias)(), alias)
538+
539+
def test_pickle(self):
540+
Callable = self.Callable
541+
alias = Callable[[int, str], float]
542+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
543+
s = pickle.dumps(alias, proto)
544+
loaded = pickle.loads(s)
545+
self.assertEqual(alias.__origin__, loaded.__origin__)
546+
self.assertEqual(alias.__args__, loaded.__args__)
547+
self.assertEqual(alias.__parameters__, loaded.__parameters__)
548+
549+
def test_var_substitution(self):
550+
Callable = self.Callable
551+
fullname = f"{Callable.__module__}.Callable"
552+
C1 = Callable[[int, T], T]
553+
C2 = Callable[[KT, T], VT]
554+
C3 = Callable[..., T]
555+
self.assertEqual(C1[str], Callable[[int, str], str])
556+
self.assertEqual(C2[int, float, str], Callable[[int, float], str])
557+
self.assertEqual(C3[int], Callable[..., int])
558+
559+
# multi chaining
560+
C4 = C2[int, VT, str]
561+
self.assertEqual(repr(C4), f"{fullname}[[int, ~VT], str]")
562+
self.assertEqual(repr(C4[dict]), f"{fullname}[[int, dict], str]")
563+
self.assertEqual(C4[dict], Callable[[int, dict], str])
564+
565+
# substitute a nested GenericAlias (both typing and the builtin
566+
# version)
567+
C5 = Callable[[typing.List[T], tuple[KT, T], VT], int]
568+
self.assertEqual(C5[int, str, float],
569+
Callable[[typing.List[int], tuple[str, int], float], int])
570+
571+
def test_type_erasure(self):
572+
Callable = self.Callable
573+
class C1(Callable):
574+
def __call__(self):
575+
return None
576+
a = C1[[int], T]
577+
self.assertIs(a().__class__, C1)
578+
self.assertEqual(a().__orig_class__, C1[[int], T])
579+
580+
def test_paramspec(self):
581+
Callable = self.Callable
582+
fullname = f"{Callable.__module__}.Callable"
583+
P = ParamSpec('P')
584+
C1 = Callable[P, T]
585+
# substitution
586+
self.assertEqual(C1[int, str], Callable[[int], str])
587+
self.assertEqual(C1[[int, str], str], Callable[[int, str], str])
588+
self.assertEqual(repr(C1), f"{fullname}[~P, ~T]")
589+
self.assertEqual(repr(C1[int, str]), f"{fullname}[[int], str]")
590+
591+
C2 = Callable[P, int]
592+
# special case in PEP 612 where
593+
# X[int, str, float] == X[[int, str, float]]
594+
self.assertEqual(C2[int, str, float], C2[[int, str, float]])
595+
self.assertEqual(repr(C2), f"{fullname}[~P, int]")
596+
self.assertEqual(repr(C2[int, str]), f"{fullname}[[int, str], int]")
597+
598+
def test_concatenate(self):
599+
Callable = self.Callable
600+
fullname = f"{Callable.__module__}.Callable"
601+
P = ParamSpec('P')
602+
C1 = Callable[typing.Concatenate[int, P], int]
603+
self.assertEqual(repr(C1),
604+
f"{fullname}[typing.Concatenate[int, ~P], int]")
605+
606+
def test_errors(self):
607+
Callable = self.Callable
608+
alias = Callable[[int, str], float]
609+
with self.assertRaisesRegex(TypeError, "is not a generic class"):
610+
alias[int]
611+
P = ParamSpec('P')
612+
C1 = Callable[P, T]
613+
with self.assertRaisesRegex(TypeError, "many arguments for"):
614+
C1[int, str, str]
615+
with self.assertRaisesRegex(TypeError, "few arguments for"):
616+
C1[int]
617+
618+
class TypingCallableTests(BaseCallableTests, BaseTestCase):
619+
Callable = typing.Callable
620+
621+
def test_consistency(self):
622+
# bpo-42195
623+
# Testing collections.abc.Callable's consistency with typing.Callable
624+
c1 = typing.Callable[[int, str], dict]
625+
c2 = collections.abc.Callable[[int, str], dict]
626+
self.assertEqual(c1.__args__, c2.__args__)
627+
self.assertEqual(hash(c1.__args__), hash(c2.__args__))
628+
629+
test_errors = skip("known bug #44793")(BaseCallableTests.test_errors)
630+
631+
632+
class CollectionsCallableTests(BaseCallableTests, BaseTestCase):
633+
Callable = collections.abc.Callable
634+
635+
514636
class LiteralTests(BaseTestCase):
515637
def test_basics(self):
516638
# All of these are allowed.
@@ -4496,13 +4618,6 @@ class Z(Generic[P]):
44964618
self.assertEqual(G5.__parameters__, G6.__parameters__)
44974619
self.assertEqual(G5, G6)
44984620

4499-
def test_var_substitution(self):
4500-
T = TypeVar("T")
4501-
P = ParamSpec("P")
4502-
C1 = Callable[P, T]
4503-
self.assertEqual(C1[int, str], Callable[[int], str])
4504-
self.assertEqual(C1[[int, str, dict], float], Callable[[int, str, dict], float])
4505-
45064621
def test_no_paramspec_in__parameters__(self):
45074622
# ParamSpec should not be found in __parameters__
45084623
# of generics. Usages outside Callable, Concatenate

0 commit comments

Comments
 (0)