From 8a68272e2bd80558ac6858a181024f6fd7d24011 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 3 Oct 2024 20:57:20 +0200 Subject: [PATCH 1/5] _ConcatenateGenericAlias backport to support ellipsis --- src/test_typing_extensions.py | 14 +++++++++++++- src/typing_extensions.py | 36 +++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index 8c2726f8..57698da6 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -5401,6 +5401,18 @@ def test_invalid_uses(self): ): Concatenate[1, P] + @skipUnless(TYPING_3_10_0, "Missing backported to <=3.9. See issue #48") + def test_alias_subscription_with_ellipsis(self): + P = ParamSpec('P') + X = Callable[Concatenate[int, P], Any] + + C1 = X[...] + self.assertEqual(C1.__parameters__, ()) + with self.subTest("Compare Concatenate[int, ...]"): + if sys.version_info[:2] == (3, 10): + self.skipTest("Needs Issue #110 | PR # 442: construct Concatenate with ...") + self.assertEqual(get_args(C1), (Concatenate[int, ...], Any)) + def test_basic_introspection(self): P = ParamSpec('P') C1 = Concatenate[int, P] @@ -6089,7 +6101,7 @@ def test_typing_extensions_defers_when_possible(self): if sys.version_info < (3, 10, 1): exclude |= {"Literal"} if sys.version_info < (3, 11): - exclude |= {'final', 'Any', 'NewType', 'overload'} + exclude |= {'final', 'Any', 'NewType', 'overload', 'Concatenate'} if sys.version_info < (3, 12): exclude |= { 'SupportsAbs', 'SupportsBytes', diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 5bf4f2dc..4724ae46 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -1795,6 +1795,27 @@ def __parameters__(self): return tuple( tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec)) ) +# 3.10+ +else: + _ConcatenateGenericAlias = typing._ConcatenateGenericAlias + + # 3.10 + if sys.version_info < (3, 11): + _typing_ConcatenateGenericAlias = _ConcatenateGenericAlias + + class _ConcatenateGenericAlias(_typing_ConcatenateGenericAlias, _root=True): + # needed for checks in collections.abc.Callable to accept this class + __module__ = "typing" + + def copy_with(self, params): + if isinstance(params[-1], (list, tuple)): + return (*params[:-1], *params[-1]) + if isinstance(params[-1], _ConcatenateGenericAlias): + params = (*params[:-1], *params[-1].__args__) + elif not (params[-1] is ... or isinstance(params[-1], ParamSpec)): + raise TypeError("The last parameter to Concatenate should be a " + "ParamSpec variable or ellipsis.") + return super(_typing_ConcatenateGenericAlias, self).copy_with(params) # 3.8-3.9 @@ -1804,19 +1825,22 @@ def _concatenate_getitem(self, parameters): raise TypeError("Cannot take a Concatenate of no types.") if not isinstance(parameters, tuple): parameters = (parameters,) - if not isinstance(parameters[-1], ParamSpec): + elif not (parameters[-1] is ... or isinstance(parameters[-1], ParamSpec)): raise TypeError("The last parameter to Concatenate should be a " - "ParamSpec variable.") + "ParamSpec variable or ellipsis.") msg = "Concatenate[arg, ...]: each arg must be a type." parameters = tuple(typing._type_check(p, msg) for p in parameters) + if (3, 10, 2) < sys.version_info < (3,11): + return _ConcatenateGenericAlias(self, parameters, + _typevar_types=(TypeVar, ParamSpec), + _paramspec_tvars=True) return _ConcatenateGenericAlias(self, parameters) -# 3.10+ -if hasattr(typing, 'Concatenate'): +# 3.11+ +if hasattr(typing, 'Concatenate') and sys.version_info[:2] != (3, 10): Concatenate = typing.Concatenate - _ConcatenateGenericAlias = typing._ConcatenateGenericAlias -# 3.9 +# 3.9-3.10 elif sys.version_info[:2] >= (3, 9): @_ExtensionsSpecialForm def Concatenate(self, parameters): From 0191a96ab0c613c6d597b12bf18f7e6ed6130fe2 Mon Sep 17 00:00:00 2001 From: Daraan Date: Fri, 4 Oct 2024 00:32:55 +0200 Subject: [PATCH 2/5] Apply suggestions from code review Co-authored-by: Alex Waygood --- src/typing_extensions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 4724ae46..c3dabfbb 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -1818,7 +1818,7 @@ def copy_with(self, params): return super(_typing_ConcatenateGenericAlias, self).copy_with(params) -# 3.8-3.9 +# 3.8-3.10 @typing._tp_cache def _concatenate_getitem(self, parameters): if parameters == (): @@ -1838,7 +1838,7 @@ def _concatenate_getitem(self, parameters): # 3.11+ -if hasattr(typing, 'Concatenate') and sys.version_info[:2] != (3, 10): +if sys.version_info >= (3, 11): Concatenate = typing.Concatenate # 3.9-3.10 elif sys.version_info[:2] >= (3, 9): From 611e2eb66d53f7982d3192af5983747ee71a9fa6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 4 Oct 2024 01:40:56 +0200 Subject: [PATCH 3/5] Updated changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index db6719c6..edb0d356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ subscripted objects) had wrong parameters if they were directly subscripted with an `Unpack` object. Patch by [Daraan](https://github.com/Daraan). +- Backport to Python 3.10 to support substitution with `...` on Callable + aliases that have a Concatenate argument. Patch by [Daraan](https://github.com/Daraan). # Release 4.12.2 (June 7, 2024) From 858d9b3e0da7dfe3a982139092c15757facfc241 Mon Sep 17 00:00:00 2001 From: Daraan Date: Mon, 21 Oct 2024 18:12:00 +0200 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Jelle Zijlstra --- CHANGELOG.md | 4 ++-- src/test_typing_extensions.py | 2 +- src/typing_extensions.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edb0d356..d16546c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,8 @@ subscripted objects) had wrong parameters if they were directly subscripted with an `Unpack` object. Patch by [Daraan](https://github.com/Daraan). -- Backport to Python 3.10 to support substitution with `...` on Callable - aliases that have a Concatenate argument. Patch by [Daraan](https://github.com/Daraan). +- Backport to Python 3.10 to support substitution with `...` on `Callable` + aliases that have a `Concatenate` argument. Patch by [Daraan](https://github.com/Daraan). # Release 4.12.2 (June 7, 2024) diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index 57698da6..5be0e8b4 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -5401,7 +5401,7 @@ def test_invalid_uses(self): ): Concatenate[1, P] - @skipUnless(TYPING_3_10_0, "Missing backported to <=3.9. See issue #48") + @skipUnless(TYPING_3_10_0, "Missing backport to <=3.9. See issue #48") def test_alias_subscription_with_ellipsis(self): P = ParamSpec('P') X = Callable[Concatenate[int, P], Any] diff --git a/src/typing_extensions.py b/src/typing_extensions.py index c3dabfbb..5d47205d 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -1830,7 +1830,7 @@ def _concatenate_getitem(self, parameters): "ParamSpec variable or ellipsis.") msg = "Concatenate[arg, ...]: each arg must be a type." parameters = tuple(typing._type_check(p, msg) for p in parameters) - if (3, 10, 2) < sys.version_info < (3,11): + if (3, 10, 2) < sys.version_info < (3, 11): return _ConcatenateGenericAlias(self, parameters, _typevar_types=(TypeVar, ParamSpec), _paramspec_tvars=True) From 96210d6915175d7c66c70caaf6d5e7f4f95e0a5c Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 21 Oct 2024 18:19:47 +0100 Subject: [PATCH 5/5] Apply suggestions from code review --- CHANGELOG.md | 5 +++-- src/test_typing_extensions.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bd6b635..2ddac4b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,9 @@ subscripted objects) had wrong parameters if they were directly subscripted with an `Unpack` object. Patch by [Daraan](https://github.com/Daraan). -- Backport to Python 3.10 to support substitution with `...` on `Callable` - aliases that have a `Concatenate` argument. Patch by [Daraan](https://github.com/Daraan). +- Backport to Python 3.10 the ability to substitute `...` in generic `Callable` +aliases that have a `Concatenate` special form as their argument. + Patch by [Daraan](https://github.com/Daraan). - Fix error in subscription of `Unpack` aliases causing nested Unpacks to not be resolved correctly. Patch by [Daraan](https://github.com/Daraan). diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index 1de8a9f9..dfea3e3a 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -5410,7 +5410,7 @@ def test_alias_subscription_with_ellipsis(self): self.assertEqual(C1.__parameters__, ()) with self.subTest("Compare Concatenate[int, ...]"): if sys.version_info[:2] == (3, 10): - self.skipTest("Needs Issue #110 | PR # 442: construct Concatenate with ...") + self.skipTest("Needs Issue #110 | PR #481: construct Concatenate with ...") self.assertEqual(get_args(C1), (Concatenate[int, ...], Any)) def test_basic_introspection(self):