Mailing List Archive

bpo-44794: Merge tests for typing.Callable and collection.abc.Callable (GH-27507)
https://github.com/python/cpython/commit/be4cb9089aaf58d5f90da5f9fa66dc3c6763b5a2
commit: be4cb9089aaf58d5f90da5f9fa66dc3c6763b5a2
branch: main
author: Serhiy Storchaka <storchaka@gmail.com>
committer: serhiy-storchaka <storchaka@gmail.com>
date: 2021-07-31T20:05:45+03:00
summary:

bpo-44794: Merge tests for typing.Callable and collection.abc.Callable (GH-27507)

files:
M Lib/_collections_abc.py
M Lib/test/test_genericalias.py
M Lib/test/test_typing.py

diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index bff58ad4a7f6a..33db9b259817c 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -478,8 +478,7 @@ def __getitem__(self, item):
# then X[int, str] == X[[int, str]].
param_len = len(self.__parameters__)
if param_len == 0:
- raise TypeError(f'There are no type or parameter specification'
- f'variables left in {self}')
+ raise TypeError(f'{self} is not a generic class')
if (param_len == 1
and isinstance(item, (tuple, list))
and len(item) > 1) or not isinstance(item, tuple):
diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py
index 9f927392fc874..2e70e751d4eee 100644
--- a/Lib/test/test_genericalias.py
+++ b/Lib/test/test_genericalias.py
@@ -317,96 +317,6 @@ def __new__(cls, *args, **kwargs):
with self.assertRaises(TypeError):
Bad(list, int, bad=int)

- def test_abc_callable(self):
- # A separate test is needed for Callable since it uses a subclass of
- # GenericAlias.
- alias = Callable[[int, str], float]
- with self.subTest("Testing subscription"):
- self.assertIs(alias.__origin__, Callable)
- self.assertEqual(alias.__args__, (int, str, float))
- self.assertEqual(alias.__parameters__, ())
-
- with self.subTest("Testing instance checks"):
- self.assertIsInstance(alias, GenericAlias)
-
- with self.subTest("Testing weakref"):
- self.assertEqual(ref(alias)(), alias)
-
- with self.subTest("Testing pickling"):
- s = pickle.dumps(alias)
- loaded = pickle.loads(s)
- self.assertEqual(alias.__origin__, loaded.__origin__)
- self.assertEqual(alias.__args__, loaded.__args__)
- self.assertEqual(alias.__parameters__, loaded.__parameters__)
-
- with self.subTest("Testing TypeVar substitution"):
- C1 = Callable[[int, T], T]
- C2 = Callable[[K, T], V]
- C3 = Callable[..., T]
- self.assertEqual(C1[str], Callable[[int, str], str])
- self.assertEqual(C2[int, float, str], Callable[[int, float], str])
- self.assertEqual(C3[int], Callable[..., int])
-
- # multi chaining
- C4 = C2[int, V, str]
- self.assertEqual(repr(C4).split(".")[-1], "Callable[[int, ~V], str]")
- self.assertEqual(repr(C4[dict]).split(".")[-1], "Callable[[int, dict], str]")
- self.assertEqual(C4[dict], Callable[[int, dict], str])
-
- # substitute a nested GenericAlias (both typing and the builtin
- # version)
- C5 = Callable[[typing.List[T], tuple[K, T], V], int]
- self.assertEqual(C5[int, str, float],
- Callable[[typing.List[int], tuple[str, int], float], int])
-
- with self.subTest("Testing type erasure"):
- class C1(Callable):
- def __call__(self):
- return None
- a = C1[[int], T]
- self.assertIs(a().__class__, C1)
- self.assertEqual(a().__orig_class__, C1[[int], T])
-
- # bpo-42195
- with self.subTest("Testing collections.abc.Callable's consistency "
- "with typing.Callable"):
- c1 = typing.Callable[[int, str], dict]
- c2 = Callable[[int, str], dict]
- self.assertEqual(c1.__args__, c2.__args__)
- self.assertEqual(hash(c1.__args__), hash(c2.__args__))
-
- with self.subTest("Testing ParamSpec uses"):
- P = typing.ParamSpec('P')
- C1 = Callable[P, T]
- # substitution
- self.assertEqual(C1[int, str], Callable[[int], str])
- self.assertEqual(C1[[int, str], str], Callable[[int, str], str])
- self.assertEqual(repr(C1).split(".")[-1], "Callable[~P, ~T]")
- self.assertEqual(repr(C1[int, str]).split(".")[-1], "Callable[[int], str]")
-
- C2 = Callable[P, int]
- # special case in PEP 612 where
- # X[int, str, float] == X[[int, str, float]]
- self.assertEqual(C2[int, str, float], C2[[int, str, float]])
- self.assertEqual(repr(C2).split(".")[-1], "Callable[~P, int]")
- self.assertEqual(repr(C2[int, str]).split(".")[-1], "Callable[[int, str], int]")
-
- with self.subTest("Testing Concatenate uses"):
- P = typing.ParamSpec('P')
- C1 = Callable[typing.Concatenate[int, P], int]
- self.assertEqual(repr(C1), "collections.abc.Callable"
- "[typing.Concatenate[int, ~P], int]")
-
- with self.subTest("Testing TypeErrors"):
- with self.assertRaisesRegex(TypeError, "variables left in"):
- alias[int]
- P = typing.ParamSpec('P')
- C1 = Callable[P, T]
- with self.assertRaisesRegex(TypeError, "many arguments for"):
- C1[int, str, str]
- with self.assertRaisesRegex(TypeError, "few arguments for"):
- C1[int]
-

if __name__ == "__main__":
unittest.main()
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 06bd49b593a77..fbdf634c5c3be 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -406,8 +406,8 @@ def test_basics(self):
issubclass(tuple, Tuple[int, str])

class TP(tuple): ...
- self.assertTrue(issubclass(tuple, Tuple))
- self.assertTrue(issubclass(TP, Tuple))
+ self.assertIsSubclass(tuple, Tuple)
+ self.assertIsSubclass(TP, Tuple)

def test_equality(self):
self.assertEqual(Tuple[int], Tuple[int])
@@ -418,7 +418,7 @@ def test_equality(self):
def test_tuple_subclass(self):
class MyTuple(tuple):
pass
- self.assertTrue(issubclass(MyTuple, Tuple))
+ self.assertIsSubclass(MyTuple, Tuple)

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


-class CallableTests(BaseTestCase):
+class BaseCallableTests:

def test_self_subclass(self):
+ Callable = self.Callable
with self.assertRaises(TypeError):
- self.assertTrue(issubclass(type(lambda x: x), Callable[[int], int]))
- self.assertTrue(issubclass(type(lambda x: x), Callable))
+ issubclass(types.FunctionType, Callable[[int], int])
+ self.assertIsSubclass(types.FunctionType, Callable)

def test_eq_hash(self):
- self.assertEqual(Callable[[int], int], Callable[[int], int])
- self.assertEqual(len({Callable[[int], int], Callable[[int], int]}), 1)
- self.assertNotEqual(Callable[[int], int], Callable[[int], str])
- self.assertNotEqual(Callable[[int], int], Callable[[str], int])
- self.assertNotEqual(Callable[[int], int], Callable[[int, int], int])
- self.assertNotEqual(Callable[[int], int], Callable[[], int])
- self.assertNotEqual(Callable[[int], int], Callable)
+ Callable = self.Callable
+ C = Callable[[int], int]
+ self.assertEqual(C, Callable[[int], int])
+ self.assertEqual(len({C, Callable[[int], int]}), 1)
+ self.assertNotEqual(C, Callable[[int], str])
+ self.assertNotEqual(C, Callable[[str], int])
+ self.assertNotEqual(C, Callable[[int, int], int])
+ self.assertNotEqual(C, Callable[[], int])
+ self.assertNotEqual(C, Callable[..., int])
+ self.assertNotEqual(C, Callable)

def test_cannot_instantiate(self):
+ Callable = self.Callable
with self.assertRaises(TypeError):
Callable()
with self.assertRaises(TypeError):
@@ -467,16 +472,19 @@ def test_cannot_instantiate(self):
type(c)()

def test_callable_wrong_forms(self):
+ Callable = self.Callable
with self.assertRaises(TypeError):
Callable[int]

def test_callable_instance_works(self):
+ Callable = self.Callable
def f():
pass
self.assertIsInstance(f, Callable)
self.assertNotIsInstance(None, Callable)

def test_callable_instance_type_error(self):
+ Callable = self.Callable
def f():
pass
with self.assertRaises(TypeError):
@@ -489,17 +497,19 @@ def f():
self.assertNotIsInstance(None, Callable[[], Any])

def test_repr(self):
+ Callable = self.Callable
+ fullname = f'{Callable.__module__}.Callable'
ct0 = Callable[[], bool]
- self.assertEqual(repr(ct0), 'typing.Callable[[], bool]')
+ self.assertEqual(repr(ct0), f'{fullname}[[], bool]')
ct2 = Callable[[str, float], int]
- self.assertEqual(repr(ct2), 'typing.Callable[[str, float], int]')
+ self.assertEqual(repr(ct2), f'{fullname}[[str, float], int]')
ctv = Callable[..., str]
- self.assertEqual(repr(ctv), 'typing.Callable[..., str]')
+ self.assertEqual(repr(ctv), f'{fullname}[..., str]')
ct3 = Callable[[str, float], list[int]]
- self.assertEqual(repr(ct3), 'typing.Callable[[str, float], list[int]]')
+ self.assertEqual(repr(ct3), f'{fullname}[[str, float], list[int]]')

def test_callable_with_ellipsis(self):
-
+ Callable = self.Callable
def foo(a: Callable[..., T]):
pass

@@ -507,10 +517,122 @@ def foo(a: Callable[..., T]):
{'a': Callable[..., T]})

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


+ def test_basic(self):
+ Callable = self.Callable
+ alias = Callable[[int, str], float]
+ if Callable is collections.abc.Callable:
+ self.assertIsInstance(alias, types.GenericAlias)
+ self.assertIs(alias.__origin__, collections.abc.Callable)
+ self.assertEqual(alias.__args__, (int, str, float))
+ self.assertEqual(alias.__parameters__, ())
+
+ def test_weakref(self):
+ Callable = self.Callable
+ alias = Callable[[int, str], float]
+ self.assertEqual(weakref.ref(alias)(), alias)
+
+ def test_pickle(self):
+ Callable = self.Callable
+ alias = Callable[[int, str], float]
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ s = pickle.dumps(alias, proto)
+ loaded = pickle.loads(s)
+ self.assertEqual(alias.__origin__, loaded.__origin__)
+ self.assertEqual(alias.__args__, loaded.__args__)
+ self.assertEqual(alias.__parameters__, loaded.__parameters__)
+
+ def test_var_substitution(self):
+ Callable = self.Callable
+ fullname = f"{Callable.__module__}.Callable"
+ C1 = Callable[[int, T], T]
+ C2 = Callable[[KT, T], VT]
+ C3 = Callable[..., T]
+ self.assertEqual(C1[str], Callable[[int, str], str])
+ self.assertEqual(C2[int, float, str], Callable[[int, float], str])
+ self.assertEqual(C3[int], Callable[..., int])
+
+ # multi chaining
+ C4 = C2[int, VT, str]
+ self.assertEqual(repr(C4), f"{fullname}[[int, ~VT], str]")
+ self.assertEqual(repr(C4[dict]), f"{fullname}[[int, dict], str]")
+ self.assertEqual(C4[dict], Callable[[int, dict], str])
+
+ # substitute a nested GenericAlias (both typing and the builtin
+ # version)
+ C5 = Callable[[typing.List[T], tuple[KT, T], VT], int]
+ self.assertEqual(C5[int, str, float],
+ Callable[[typing.List[int], tuple[str, int], float], int])
+
+ def test_type_erasure(self):
+ Callable = self.Callable
+ class C1(Callable):
+ def __call__(self):
+ return None
+ a = C1[[int], T]
+ self.assertIs(a().__class__, C1)
+ self.assertEqual(a().__orig_class__, C1[[int], T])
+
+ def test_paramspec(self):
+ Callable = self.Callable
+ fullname = f"{Callable.__module__}.Callable"
+ P = ParamSpec('P')
+ C1 = Callable[P, T]
+ # substitution
+ self.assertEqual(C1[int, str], Callable[[int], str])
+ self.assertEqual(C1[[int, str], str], Callable[[int, str], str])
+ self.assertEqual(repr(C1), f"{fullname}[~P, ~T]")
+ self.assertEqual(repr(C1[int, str]), f"{fullname}[[int], str]")
+
+ C2 = Callable[P, int]
+ # special case in PEP 612 where
+ # X[int, str, float] == X[[int, str, float]]
+ self.assertEqual(C2[int, str, float], C2[[int, str, float]])
+ self.assertEqual(repr(C2), f"{fullname}[~P, int]")
+ self.assertEqual(repr(C2[int, str]), f"{fullname}[[int, str], int]")
+
+ def test_concatenate(self):
+ Callable = self.Callable
+ fullname = f"{Callable.__module__}.Callable"
+ P = ParamSpec('P')
+ C1 = Callable[typing.Concatenate[int, P], int]
+ self.assertEqual(repr(C1),
+ f"{fullname}[typing.Concatenate[int, ~P], int]")
+
+ def test_errors(self):
+ Callable = self.Callable
+ alias = Callable[[int, str], float]
+ with self.assertRaisesRegex(TypeError, "is not a generic class"):
+ alias[int]
+ P = ParamSpec('P')
+ C1 = Callable[P, T]
+ with self.assertRaisesRegex(TypeError, "many arguments for"):
+ C1[int, str, str]
+ with self.assertRaisesRegex(TypeError, "few arguments for"):
+ C1[int]
+
+class TypingCallableTests(BaseCallableTests, BaseTestCase):
+ Callable = typing.Callable
+
+ def test_consistency(self):
+ # bpo-42195
+ # Testing collections.abc.Callable's consistency with typing.Callable
+ c1 = typing.Callable[[int, str], dict]
+ c2 = collections.abc.Callable[[int, str], dict]
+ self.assertEqual(c1.__args__, c2.__args__)
+ self.assertEqual(hash(c1.__args__), hash(c2.__args__))
+
+ test_errors = skip("known bug #44793")(BaseCallableTests.test_errors)
+
+
+class CollectionsCallableTests(BaseCallableTests, BaseTestCase):
+ Callable = collections.abc.Callable
+
+
class LiteralTests(BaseTestCase):
def test_basics(self):
# All of these are allowed.
@@ -4496,13 +4618,6 @@ class Z(Generic[P]):
self.assertEqual(G5.__parameters__, G6.__parameters__)
self.assertEqual(G5, G6)

- def test_var_substitution(self):
- T = TypeVar("T")
- P = ParamSpec("P")
- C1 = Callable[P, T]
- self.assertEqual(C1[int, str], Callable[[int], str])
- self.assertEqual(C1[[int, str, dict], float], Callable[[int, str, dict], float])
-
def test_no_paramspec_in__parameters__(self):
# ParamSpec should not be found in __parameters__
# of generics. Usages outside Callable, Concatenate

_______________________________________________
Python-checkins mailing list
Python-checkins@python.org
https://mail.python.org/mailman/listinfo/python-checkins
bpo-44794: Merge tests for typing.Callable and collection.abc.Callable (GH-27507) [ In reply to ]
https://github.com/python/cpython/commit/76903ff9ce2d28f542c44ba97aa043dde8b55daa
commit: 76903ff9ce2d28f542c44ba97aa043dde8b55daa
branch: 3.10
author: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
committer: miss-islington <31488909+miss-islington@users.noreply.github.com>
date: 2021-07-31T10:25:22-07:00
summary:

bpo-44794: Merge tests for typing.Callable and collection.abc.Callable (GH-27507)

(cherry picked from commit be4cb9089aaf58d5f90da5f9fa66dc3c6763b5a2)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>

files:
M Lib/_collections_abc.py
M Lib/test/test_genericalias.py
M Lib/test/test_typing.py

diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index bff58ad4a7f6a..33db9b259817c 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -478,8 +478,7 @@ def __getitem__(self, item):
# then X[int, str] == X[[int, str]].
param_len = len(self.__parameters__)
if param_len == 0:
- raise TypeError(f'There are no type or parameter specification'
- f'variables left in {self}')
+ raise TypeError(f'{self} is not a generic class')
if (param_len == 1
and isinstance(item, (tuple, list))
and len(item) > 1) or not isinstance(item, tuple):
diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py
index 9f927392fc874..2e70e751d4eee 100644
--- a/Lib/test/test_genericalias.py
+++ b/Lib/test/test_genericalias.py
@@ -317,96 +317,6 @@ def __new__(cls, *args, **kwargs):
with self.assertRaises(TypeError):
Bad(list, int, bad=int)

- def test_abc_callable(self):
- # A separate test is needed for Callable since it uses a subclass of
- # GenericAlias.
- alias = Callable[[int, str], float]
- with self.subTest("Testing subscription"):
- self.assertIs(alias.__origin__, Callable)
- self.assertEqual(alias.__args__, (int, str, float))
- self.assertEqual(alias.__parameters__, ())
-
- with self.subTest("Testing instance checks"):
- self.assertIsInstance(alias, GenericAlias)
-
- with self.subTest("Testing weakref"):
- self.assertEqual(ref(alias)(), alias)
-
- with self.subTest("Testing pickling"):
- s = pickle.dumps(alias)
- loaded = pickle.loads(s)
- self.assertEqual(alias.__origin__, loaded.__origin__)
- self.assertEqual(alias.__args__, loaded.__args__)
- self.assertEqual(alias.__parameters__, loaded.__parameters__)
-
- with self.subTest("Testing TypeVar substitution"):
- C1 = Callable[[int, T], T]
- C2 = Callable[[K, T], V]
- C3 = Callable[..., T]
- self.assertEqual(C1[str], Callable[[int, str], str])
- self.assertEqual(C2[int, float, str], Callable[[int, float], str])
- self.assertEqual(C3[int], Callable[..., int])
-
- # multi chaining
- C4 = C2[int, V, str]
- self.assertEqual(repr(C4).split(".")[-1], "Callable[[int, ~V], str]")
- self.assertEqual(repr(C4[dict]).split(".")[-1], "Callable[[int, dict], str]")
- self.assertEqual(C4[dict], Callable[[int, dict], str])
-
- # substitute a nested GenericAlias (both typing and the builtin
- # version)
- C5 = Callable[[typing.List[T], tuple[K, T], V], int]
- self.assertEqual(C5[int, str, float],
- Callable[[typing.List[int], tuple[str, int], float], int])
-
- with self.subTest("Testing type erasure"):
- class C1(Callable):
- def __call__(self):
- return None
- a = C1[[int], T]
- self.assertIs(a().__class__, C1)
- self.assertEqual(a().__orig_class__, C1[[int], T])
-
- # bpo-42195
- with self.subTest("Testing collections.abc.Callable's consistency "
- "with typing.Callable"):
- c1 = typing.Callable[[int, str], dict]
- c2 = Callable[[int, str], dict]
- self.assertEqual(c1.__args__, c2.__args__)
- self.assertEqual(hash(c1.__args__), hash(c2.__args__))
-
- with self.subTest("Testing ParamSpec uses"):
- P = typing.ParamSpec('P')
- C1 = Callable[P, T]
- # substitution
- self.assertEqual(C1[int, str], Callable[[int], str])
- self.assertEqual(C1[[int, str], str], Callable[[int, str], str])
- self.assertEqual(repr(C1).split(".")[-1], "Callable[~P, ~T]")
- self.assertEqual(repr(C1[int, str]).split(".")[-1], "Callable[[int], str]")
-
- C2 = Callable[P, int]
- # special case in PEP 612 where
- # X[int, str, float] == X[[int, str, float]]
- self.assertEqual(C2[int, str, float], C2[[int, str, float]])
- self.assertEqual(repr(C2).split(".")[-1], "Callable[~P, int]")
- self.assertEqual(repr(C2[int, str]).split(".")[-1], "Callable[[int, str], int]")
-
- with self.subTest("Testing Concatenate uses"):
- P = typing.ParamSpec('P')
- C1 = Callable[typing.Concatenate[int, P], int]
- self.assertEqual(repr(C1), "collections.abc.Callable"
- "[typing.Concatenate[int, ~P], int]")
-
- with self.subTest("Testing TypeErrors"):
- with self.assertRaisesRegex(TypeError, "variables left in"):
- alias[int]
- P = typing.ParamSpec('P')
- C1 = Callable[P, T]
- with self.assertRaisesRegex(TypeError, "many arguments for"):
- C1[int, str, str]
- with self.assertRaisesRegex(TypeError, "few arguments for"):
- C1[int]
-

if __name__ == "__main__":
unittest.main()
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 3bc9b9cb8e6ed..029a1586a02a1 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -400,8 +400,8 @@ def test_basics(self):
issubclass(tuple, Tuple[int, str])

class TP(tuple): ...
- self.assertTrue(issubclass(tuple, Tuple))
- self.assertTrue(issubclass(TP, Tuple))
+ self.assertIsSubclass(tuple, Tuple)
+ self.assertIsSubclass(TP, Tuple)

def test_equality(self):
self.assertEqual(Tuple[int], Tuple[int])
@@ -412,7 +412,7 @@ def test_equality(self):
def test_tuple_subclass(self):
class MyTuple(tuple):
pass
- self.assertTrue(issubclass(MyTuple, Tuple))
+ self.assertIsSubclass(MyTuple, Tuple)

def test_tuple_instance_type_error(self):
with self.assertRaises(TypeError):
@@ -433,23 +433,28 @@ def test_errors(self):
issubclass(42, Tuple[int])


-class CallableTests(BaseTestCase):
+class BaseCallableTests:

def test_self_subclass(self):
+ Callable = self.Callable
with self.assertRaises(TypeError):
- self.assertTrue(issubclass(type(lambda x: x), Callable[[int], int]))
- self.assertTrue(issubclass(type(lambda x: x), Callable))
+ issubclass(types.FunctionType, Callable[[int], int])
+ self.assertIsSubclass(types.FunctionType, Callable)

def test_eq_hash(self):
- self.assertEqual(Callable[[int], int], Callable[[int], int])
- self.assertEqual(len({Callable[[int], int], Callable[[int], int]}), 1)
- self.assertNotEqual(Callable[[int], int], Callable[[int], str])
- self.assertNotEqual(Callable[[int], int], Callable[[str], int])
- self.assertNotEqual(Callable[[int], int], Callable[[int, int], int])
- self.assertNotEqual(Callable[[int], int], Callable[[], int])
- self.assertNotEqual(Callable[[int], int], Callable)
+ Callable = self.Callable
+ C = Callable[[int], int]
+ self.assertEqual(C, Callable[[int], int])
+ self.assertEqual(len({C, Callable[[int], int]}), 1)
+ self.assertNotEqual(C, Callable[[int], str])
+ self.assertNotEqual(C, Callable[[str], int])
+ self.assertNotEqual(C, Callable[[int, int], int])
+ self.assertNotEqual(C, Callable[[], int])
+ self.assertNotEqual(C, Callable[..., int])
+ self.assertNotEqual(C, Callable)

def test_cannot_instantiate(self):
+ Callable = self.Callable
with self.assertRaises(TypeError):
Callable()
with self.assertRaises(TypeError):
@@ -461,16 +466,19 @@ def test_cannot_instantiate(self):
type(c)()

def test_callable_wrong_forms(self):
+ Callable = self.Callable
with self.assertRaises(TypeError):
Callable[int]

def test_callable_instance_works(self):
+ Callable = self.Callable
def f():
pass
self.assertIsInstance(f, Callable)
self.assertNotIsInstance(None, Callable)

def test_callable_instance_type_error(self):
+ Callable = self.Callable
def f():
pass
with self.assertRaises(TypeError):
@@ -483,17 +491,19 @@ def f():
self.assertNotIsInstance(None, Callable[[], Any])

def test_repr(self):
+ Callable = self.Callable
+ fullname = f'{Callable.__module__}.Callable'
ct0 = Callable[[], bool]
- self.assertEqual(repr(ct0), 'typing.Callable[[], bool]')
+ self.assertEqual(repr(ct0), f'{fullname}[[], bool]')
ct2 = Callable[[str, float], int]
- self.assertEqual(repr(ct2), 'typing.Callable[[str, float], int]')
+ self.assertEqual(repr(ct2), f'{fullname}[[str, float], int]')
ctv = Callable[..., str]
- self.assertEqual(repr(ctv), 'typing.Callable[..., str]')
+ self.assertEqual(repr(ctv), f'{fullname}[..., str]')
ct3 = Callable[[str, float], list[int]]
- self.assertEqual(repr(ct3), 'typing.Callable[[str, float], list[int]]')
+ self.assertEqual(repr(ct3), f'{fullname}[[str, float], list[int]]')

def test_callable_with_ellipsis(self):
-
+ Callable = self.Callable
def foo(a: Callable[..., T]):
pass

@@ -501,10 +511,122 @@ def foo(a: Callable[..., T]):
{'a': Callable[..., T]})

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


+ def test_basic(self):
+ Callable = self.Callable
+ alias = Callable[[int, str], float]
+ if Callable is collections.abc.Callable:
+ self.assertIsInstance(alias, types.GenericAlias)
+ self.assertIs(alias.__origin__, collections.abc.Callable)
+ self.assertEqual(alias.__args__, (int, str, float))
+ self.assertEqual(alias.__parameters__, ())
+
+ def test_weakref(self):
+ Callable = self.Callable
+ alias = Callable[[int, str], float]
+ self.assertEqual(weakref.ref(alias)(), alias)
+
+ def test_pickle(self):
+ Callable = self.Callable
+ alias = Callable[[int, str], float]
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ s = pickle.dumps(alias, proto)
+ loaded = pickle.loads(s)
+ self.assertEqual(alias.__origin__, loaded.__origin__)
+ self.assertEqual(alias.__args__, loaded.__args__)
+ self.assertEqual(alias.__parameters__, loaded.__parameters__)
+
+ def test_var_substitution(self):
+ Callable = self.Callable
+ fullname = f"{Callable.__module__}.Callable"
+ C1 = Callable[[int, T], T]
+ C2 = Callable[[KT, T], VT]
+ C3 = Callable[..., T]
+ self.assertEqual(C1[str], Callable[[int, str], str])
+ self.assertEqual(C2[int, float, str], Callable[[int, float], str])
+ self.assertEqual(C3[int], Callable[..., int])
+
+ # multi chaining
+ C4 = C2[int, VT, str]
+ self.assertEqual(repr(C4), f"{fullname}[[int, ~VT], str]")
+ self.assertEqual(repr(C4[dict]), f"{fullname}[[int, dict], str]")
+ self.assertEqual(C4[dict], Callable[[int, dict], str])
+
+ # substitute a nested GenericAlias (both typing and the builtin
+ # version)
+ C5 = Callable[[typing.List[T], tuple[KT, T], VT], int]
+ self.assertEqual(C5[int, str, float],
+ Callable[[typing.List[int], tuple[str, int], float], int])
+
+ def test_type_erasure(self):
+ Callable = self.Callable
+ class C1(Callable):
+ def __call__(self):
+ return None
+ a = C1[[int], T]
+ self.assertIs(a().__class__, C1)
+ self.assertEqual(a().__orig_class__, C1[[int], T])
+
+ def test_paramspec(self):
+ Callable = self.Callable
+ fullname = f"{Callable.__module__}.Callable"
+ P = ParamSpec('P')
+ C1 = Callable[P, T]
+ # substitution
+ self.assertEqual(C1[int, str], Callable[[int], str])
+ self.assertEqual(C1[[int, str], str], Callable[[int, str], str])
+ self.assertEqual(repr(C1), f"{fullname}[~P, ~T]")
+ self.assertEqual(repr(C1[int, str]), f"{fullname}[[int], str]")
+
+ C2 = Callable[P, int]
+ # special case in PEP 612 where
+ # X[int, str, float] == X[[int, str, float]]
+ self.assertEqual(C2[int, str, float], C2[[int, str, float]])
+ self.assertEqual(repr(C2), f"{fullname}[~P, int]")
+ self.assertEqual(repr(C2[int, str]), f"{fullname}[[int, str], int]")
+
+ def test_concatenate(self):
+ Callable = self.Callable
+ fullname = f"{Callable.__module__}.Callable"
+ P = ParamSpec('P')
+ C1 = Callable[typing.Concatenate[int, P], int]
+ self.assertEqual(repr(C1),
+ f"{fullname}[typing.Concatenate[int, ~P], int]")
+
+ def test_errors(self):
+ Callable = self.Callable
+ alias = Callable[[int, str], float]
+ with self.assertRaisesRegex(TypeError, "is not a generic class"):
+ alias[int]
+ P = ParamSpec('P')
+ C1 = Callable[P, T]
+ with self.assertRaisesRegex(TypeError, "many arguments for"):
+ C1[int, str, str]
+ with self.assertRaisesRegex(TypeError, "few arguments for"):
+ C1[int]
+
+class TypingCallableTests(BaseCallableTests, BaseTestCase):
+ Callable = typing.Callable
+
+ def test_consistency(self):
+ # bpo-42195
+ # Testing collections.abc.Callable's consistency with typing.Callable
+ c1 = typing.Callable[[int, str], dict]
+ c2 = collections.abc.Callable[[int, str], dict]
+ self.assertEqual(c1.__args__, c2.__args__)
+ self.assertEqual(hash(c1.__args__), hash(c2.__args__))
+
+ test_errors = skip("known bug #44793")(BaseCallableTests.test_errors)
+
+
+class CollectionsCallableTests(BaseCallableTests, BaseTestCase):
+ Callable = collections.abc.Callable
+
+
class LiteralTests(BaseTestCase):
def test_basics(self):
# All of these are allowed.
@@ -4456,13 +4578,6 @@ class Z(Generic[P]):
self.assertEqual(G5.__parameters__, G6.__parameters__)
self.assertEqual(G5, G6)

- def test_var_substitution(self):
- T = TypeVar("T")
- P = ParamSpec("P")
- C1 = Callable[P, T]
- self.assertEqual(C1[int, str], Callable[[int], str])
- self.assertEqual(C1[[int, str, dict], float], Callable[[int, str, dict], float])
-
def test_no_paramspec_in__parameters__(self):
# ParamSpec should not be found in __parameters__
# of generics. Usages outside Callable, Concatenate

_______________________________________________
Python-checkins mailing list
Python-checkins@python.org
https://mail.python.org/mailman/listinfo/python-checkins