Skip to content

Commit 4de1e65

Browse files
sobolevnAlexWaygood
authored andcommitted
pythongh-102615: Use list instead of tuple in repr of paramspec (python#102637)
Co-authored-by: Alex Waygood <[email protected]>
1 parent ff52779 commit 4de1e65

File tree

3 files changed

+52
-3
lines changed

3 files changed

+52
-3
lines changed

Lib/test/test_typing.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3809,6 +3809,51 @@ class Y(C[int]):
38093809
self.assertEqual(Y.__qualname__,
38103810
'GenericTests.test_repr_2.<locals>.Y')
38113811

3812+
def test_repr_3(self):
3813+
T = TypeVar('T')
3814+
T1 = TypeVar('T1')
3815+
P = ParamSpec('P')
3816+
P2 = ParamSpec('P2')
3817+
Ts = TypeVarTuple('Ts')
3818+
3819+
class MyCallable(Generic[P, T]):
3820+
pass
3821+
3822+
class DoubleSpec(Generic[P, P2, T]):
3823+
pass
3824+
3825+
class TsP(Generic[*Ts, P]):
3826+
pass
3827+
3828+
object_to_expected_repr = {
3829+
MyCallable[P, T]: "MyCallable[~P, ~T]",
3830+
MyCallable[Concatenate[T1, P], T]: "MyCallable[typing.Concatenate[~T1, ~P], ~T]",
3831+
MyCallable[[], bool]: "MyCallable[[], bool]",
3832+
MyCallable[[int], bool]: "MyCallable[[int], bool]",
3833+
MyCallable[[int, str], bool]: "MyCallable[[int, str], bool]",
3834+
MyCallable[[int, list[int]], bool]: "MyCallable[[int, list[int]], bool]",
3835+
MyCallable[Concatenate[*Ts, P], T]: "MyCallable[typing.Concatenate[*Ts, ~P], ~T]",
3836+
3837+
DoubleSpec[P2, P, T]: "DoubleSpec[~P2, ~P, ~T]",
3838+
DoubleSpec[[int], [str], bool]: "DoubleSpec[[int], [str], bool]",
3839+
DoubleSpec[[int, int], [str, str], bool]: "DoubleSpec[[int, int], [str, str], bool]",
3840+
3841+
TsP[*Ts, P]: "TsP[*Ts, ~P]",
3842+
TsP[int, str, list[int], []]: "TsP[int, str, list[int], []]",
3843+
TsP[int, [str, list[int]]]: "TsP[int, [str, list[int]]]",
3844+
3845+
# These lines are just too long to fit:
3846+
MyCallable[Concatenate[*Ts, P], int][int, str, [bool, float]]:
3847+
"MyCallable[[int, str, bool, float], int]",
3848+
}
3849+
3850+
for obj, expected_repr in object_to_expected_repr.items():
3851+
with self.subTest(obj=obj, expected_repr=expected_repr):
3852+
self.assertRegex(
3853+
repr(obj),
3854+
fr"^{re.escape(MyCallable.__module__)}.*\.{re.escape(expected_repr)}$",
3855+
)
3856+
38123857
def test_eq_1(self):
38133858
self.assertEqual(Generic, Generic)
38143859
self.assertEqual(Generic[T], Generic[T])

Lib/typing.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,16 +230,17 @@ def _type_repr(obj):
230230
typically enough to uniquely identify a type. For everything
231231
else, we fall back on repr(obj).
232232
"""
233-
if isinstance(obj, types.GenericAlias):
234-
return repr(obj)
235233
if isinstance(obj, type):
236234
if obj.__module__ == 'builtins':
237235
return obj.__qualname__
238236
return f'{obj.__module__}.{obj.__qualname__}'
239237
if obj is ...:
240-
return('...')
238+
return '...'
241239
if isinstance(obj, types.FunctionType):
242240
return obj.__name__
241+
if isinstance(obj, tuple):
242+
# Special case for `repr` of types with `ParamSpec`:
243+
return '[' + ', '.join(_type_repr(t) for t in obj) + ']'
243244
return repr(obj)
244245

245246

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Typing: Improve the ``repr`` of generic aliases for classes generic over a
2+
:class:`~typing.ParamSpec`. (Use square brackets to represent a parameter
3+
list.)

0 commit comments

Comments
 (0)