Skip to content

Commit

Permalink
pythongh-102615: Use list instead of tuple in repr of paramspec (
Browse files Browse the repository at this point in the history
…python#102637)

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
  • Loading branch information
2 people authored and Fidget-Spinner committed Mar 27, 2023
1 parent ff52779 commit 4de1e65
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 3 deletions.
45 changes: 45 additions & 0 deletions Lib/test/test_typing.py
Expand Up @@ -3809,6 +3809,51 @@ class Y(C[int]):
self.assertEqual(Y.__qualname__,
'GenericTests.test_repr_2.<locals>.Y')

def test_repr_3(self):
T = TypeVar('T')
T1 = TypeVar('T1')
P = ParamSpec('P')
P2 = ParamSpec('P2')
Ts = TypeVarTuple('Ts')

class MyCallable(Generic[P, T]):
pass

class DoubleSpec(Generic[P, P2, T]):
pass

class TsP(Generic[*Ts, P]):
pass

object_to_expected_repr = {
MyCallable[P, T]: "MyCallable[~P, ~T]",
MyCallable[Concatenate[T1, P], T]: "MyCallable[typing.Concatenate[~T1, ~P], ~T]",
MyCallable[[], bool]: "MyCallable[[], bool]",
MyCallable[[int], bool]: "MyCallable[[int], bool]",
MyCallable[[int, str], bool]: "MyCallable[[int, str], bool]",
MyCallable[[int, list[int]], bool]: "MyCallable[[int, list[int]], bool]",
MyCallable[Concatenate[*Ts, P], T]: "MyCallable[typing.Concatenate[*Ts, ~P], ~T]",

DoubleSpec[P2, P, T]: "DoubleSpec[~P2, ~P, ~T]",
DoubleSpec[[int], [str], bool]: "DoubleSpec[[int], [str], bool]",
DoubleSpec[[int, int], [str, str], bool]: "DoubleSpec[[int, int], [str, str], bool]",

TsP[*Ts, P]: "TsP[*Ts, ~P]",
TsP[int, str, list[int], []]: "TsP[int, str, list[int], []]",
TsP[int, [str, list[int]]]: "TsP[int, [str, list[int]]]",

# These lines are just too long to fit:
MyCallable[Concatenate[*Ts, P], int][int, str, [bool, float]]:
"MyCallable[[int, str, bool, float], int]",
}

for obj, expected_repr in object_to_expected_repr.items():
with self.subTest(obj=obj, expected_repr=expected_repr):
self.assertRegex(
repr(obj),
fr"^{re.escape(MyCallable.__module__)}.*\.{re.escape(expected_repr)}$",
)

def test_eq_1(self):
self.assertEqual(Generic, Generic)
self.assertEqual(Generic[T], Generic[T])
Expand Down
7 changes: 4 additions & 3 deletions Lib/typing.py
Expand Up @@ -230,16 +230,17 @@ def _type_repr(obj):
typically enough to uniquely identify a type. For everything
else, we fall back on repr(obj).
"""
if isinstance(obj, types.GenericAlias):
return repr(obj)
if isinstance(obj, type):
if obj.__module__ == 'builtins':
return obj.__qualname__
return f'{obj.__module__}.{obj.__qualname__}'
if obj is ...:
return('...')
return '...'
if isinstance(obj, types.FunctionType):
return obj.__name__
if isinstance(obj, tuple):
# Special case for `repr` of types with `ParamSpec`:
return '[' + ', '.join(_type_repr(t) for t in obj) + ']'
return repr(obj)


Expand Down
@@ -0,0 +1,3 @@
Typing: Improve the ``repr`` of generic aliases for classes generic over a
:class:`~typing.ParamSpec`. (Use square brackets to represent a parameter
list.)

0 comments on commit 4de1e65

Please sign in to comment.