[Python-checkins] bpo-42195: Override _CallableGenericAlias's __getitem__ (GH-23915)

miss-islington webhook-mailer at python.org
Wed Dec 23 21:47:49 EST 2020

commit: 6dd3da3cf4a0d6cb62d9c2a155434c127183454d
branch: master
author: kj <28750310+Fidget-Spinner at users.noreply.github.com>
committer: miss-islington <31488909+miss-islington at users.noreply.github.com>
date: 2020-12-23T18:47:40-08:00

bpo-42195: Override _CallableGenericAlias's __getitem__ (GH-23915)

Added `__getitem__` for `_CallableGenericAlias` so that it returns a subclass (itself) of `types.GenericAlias` rather than the default behavior of returning a plain `types.GenericAlias`. This fixes `repr` issues occuring after `TypeVar` substitution arising from the previous behavior.

M Lib/_collections_abc.py
M Lib/test/test_genericalias.py

diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index 7c3faa64ea7f9..e4eac79646235 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -434,7 +434,7 @@ def __create_ga(cls, origin, args):
             raise TypeError(
                 "Callable must be used as Callable[[arg, ...], result].")
         t_args, t_result = args
-        if isinstance(t_args, list):
+        if isinstance(t_args, (list, tuple)):
             ga_args = tuple(t_args) + (t_result,)
         # This relaxes what t_args can be on purpose to allow things like
         # PEP 612 ParamSpec.  Responsibility for whether a user is using
@@ -456,6 +456,16 @@ def __reduce__(self):
             args = list(args[:-1]), args[-1]
         return _CallableGenericAlias, (Callable, args)
+    def __getitem__(self, item):
+        # Called during TypeVar substitution, returns the custom subclass
+        # rather than the default types.GenericAlias object.
+        ga = super().__getitem__(item)
+        args = ga.__args__
+        t_result = args[-1]
+        t_args = args[:-1]
+        args = (t_args, t_result)
+        return _CallableGenericAlias(Callable, args)
 def _type_repr(obj):
     """Return the repr() of an object, special-casing types (internal helper).
diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py
index 5de13fe6d2f68..ccf40b13d3a94 100644
--- a/Lib/test/test_genericalias.py
+++ b/Lib/test/test_genericalias.py
@@ -347,6 +347,12 @@ def test_abc_callable(self):
             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])
         with self.subTest("Testing type erasure"):
             class C1(Callable):
                 def __call__(self):

More information about the Python-checkins mailing list