Skip to content

Commit a125198

Browse files
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. (cherry picked from commit 6dd3da3) Co-authored-by: kj <[email protected]>
1 parent 1e1bacf commit a125198

File tree

2 files changed

+17
-1
lines changed

2 files changed

+17
-1
lines changed

Lib/_collections_abc.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ def __create_ga(cls, origin, args):
441441
raise TypeError(
442442
"Callable must be used as Callable[[arg, ...], result].")
443443
t_args, t_result = args
444-
if isinstance(t_args, list):
444+
if isinstance(t_args, (list, tuple)):
445445
ga_args = tuple(t_args) + (t_result,)
446446
# This relaxes what t_args can be on purpose to allow things like
447447
# PEP 612 ParamSpec. Responsibility for whether a user is using
@@ -463,6 +463,16 @@ def __reduce__(self):
463463
args = list(args[:-1]), args[-1]
464464
return _CallableGenericAlias, (Callable, args)
465465

466+
def __getitem__(self, item):
467+
# Called during TypeVar substitution, returns the custom subclass
468+
# rather than the default types.GenericAlias object.
469+
ga = super().__getitem__(item)
470+
args = ga.__args__
471+
t_result = args[-1]
472+
t_args = args[:-1]
473+
args = (t_args, t_result)
474+
return _CallableGenericAlias(Callable, args)
475+
466476

467477
def _type_repr(obj):
468478
"""Return the repr() of an object, special-casing types (internal helper).

Lib/test/test_genericalias.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,12 @@ def test_abc_callable(self):
347347
self.assertEqual(C2[int, float, str], Callable[[int, float], str])
348348
self.assertEqual(C3[int], Callable[..., int])
349349

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+
350356
with self.subTest("Testing type erasure"):
351357
class C1(Callable):
352358
def __call__(self):

0 commit comments

Comments
 (0)