diff options
author | Nikita Sobolev <mail@sobolevn.me> | 2023-03-15 09:33:41 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-15 09:33:41 (GMT) |
commit | 2b5781d659ce3ffe03d4c1f46e4875e604cf2d88 (patch) | |
tree | dda052375c1a44ffdc0dd5b610163a539c0d2ec3 | |
parent | 8647ba4b639077e201751ae6dbd82e8bfcf80895 (diff) | |
download | cpython-2b5781d659ce3ffe03d4c1f46e4875e604cf2d88.zip cpython-2b5781d659ce3ffe03d4c1f46e4875e604cf2d88.tar.gz cpython-2b5781d659ce3ffe03d4c1f46e4875e604cf2d88.tar.bz2 |
gh-102615: Use `list` instead of `tuple` in `repr` of paramspec (#102637)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
-rw-r--r-- | Lib/test/test_typing.py | 45 | ||||
-rw-r--r-- | Lib/typing.py | 7 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2023-03-13-12-05-55.gh-issue-102615.NcA_ZL.rst | 3 |
3 files changed, 52 insertions, 3 deletions
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index c17be6c..89c725c 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3809,6 +3809,51 @@ class GenericTests(BaseTestCase): 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]) diff --git a/Lib/typing.py b/Lib/typing.py index 8d40e92..ab33439 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -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) diff --git a/Misc/NEWS.d/next/Library/2023-03-13-12-05-55.gh-issue-102615.NcA_ZL.rst b/Misc/NEWS.d/next/Library/2023-03-13-12-05-55.gh-issue-102615.NcA_ZL.rst new file mode 100644 index 0000000..3330683 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-03-13-12-05-55.gh-issue-102615.NcA_ZL.rst @@ -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.) |