Skip to content

Commit 8a68272

Browse files
committed
_ConcatenateGenericAlias backport to support ellipsis
1 parent 08d866b commit 8a68272

File tree

2 files changed

+43
-7
lines changed

2 files changed

+43
-7
lines changed

src/test_typing_extensions.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5401,6 +5401,18 @@ def test_invalid_uses(self):
54015401
):
54025402
Concatenate[1, P]
54035403

5404+
@skipUnless(TYPING_3_10_0, "Missing backported to <=3.9. See issue #48")
5405+
def test_alias_subscription_with_ellipsis(self):
5406+
P = ParamSpec('P')
5407+
X = Callable[Concatenate[int, P], Any]
5408+
5409+
C1 = X[...]
5410+
self.assertEqual(C1.__parameters__, ())
5411+
with self.subTest("Compare Concatenate[int, ...]"):
5412+
if sys.version_info[:2] == (3, 10):
5413+
self.skipTest("Needs Issue #110 | PR # 442: construct Concatenate with ...")
5414+
self.assertEqual(get_args(C1), (Concatenate[int, ...], Any))
5415+
54045416
def test_basic_introspection(self):
54055417
P = ParamSpec('P')
54065418
C1 = Concatenate[int, P]
@@ -6089,7 +6101,7 @@ def test_typing_extensions_defers_when_possible(self):
60896101
if sys.version_info < (3, 10, 1):
60906102
exclude |= {"Literal"}
60916103
if sys.version_info < (3, 11):
6092-
exclude |= {'final', 'Any', 'NewType', 'overload'}
6104+
exclude |= {'final', 'Any', 'NewType', 'overload', 'Concatenate'}
60936105
if sys.version_info < (3, 12):
60946106
exclude |= {
60956107
'SupportsAbs', 'SupportsBytes',

src/typing_extensions.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,27 @@ def __parameters__(self):
17951795
return tuple(
17961796
tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec))
17971797
)
1798+
# 3.10+
1799+
else:
1800+
_ConcatenateGenericAlias = typing._ConcatenateGenericAlias
1801+
1802+
# 3.10
1803+
if sys.version_info < (3, 11):
1804+
_typing_ConcatenateGenericAlias = _ConcatenateGenericAlias
1805+
1806+
class _ConcatenateGenericAlias(_typing_ConcatenateGenericAlias, _root=True):
1807+
# needed for checks in collections.abc.Callable to accept this class
1808+
__module__ = "typing"
1809+
1810+
def copy_with(self, params):
1811+
if isinstance(params[-1], (list, tuple)):
1812+
return (*params[:-1], *params[-1])
1813+
if isinstance(params[-1], _ConcatenateGenericAlias):
1814+
params = (*params[:-1], *params[-1].__args__)
1815+
elif not (params[-1] is ... or isinstance(params[-1], ParamSpec)):
1816+
raise TypeError("The last parameter to Concatenate should be a "
1817+
"ParamSpec variable or ellipsis.")
1818+
return super(_typing_ConcatenateGenericAlias, self).copy_with(params)
17981819

17991820

18001821
# 3.8-3.9
@@ -1804,19 +1825,22 @@ def _concatenate_getitem(self, parameters):
18041825
raise TypeError("Cannot take a Concatenate of no types.")
18051826
if not isinstance(parameters, tuple):
18061827
parameters = (parameters,)
1807-
if not isinstance(parameters[-1], ParamSpec):
1828+
elif not (parameters[-1] is ... or isinstance(parameters[-1], ParamSpec)):
18081829
raise TypeError("The last parameter to Concatenate should be a "
1809-
"ParamSpec variable.")
1830+
"ParamSpec variable or ellipsis.")
18101831
msg = "Concatenate[arg, ...]: each arg must be a type."
18111832
parameters = tuple(typing._type_check(p, msg) for p in parameters)
1833+
if (3, 10, 2) < sys.version_info < (3,11):
1834+
return _ConcatenateGenericAlias(self, parameters,
1835+
_typevar_types=(TypeVar, ParamSpec),
1836+
_paramspec_tvars=True)
18121837
return _ConcatenateGenericAlias(self, parameters)
18131838

18141839

1815-
# 3.10+
1816-
if hasattr(typing, 'Concatenate'):
1840+
# 3.11+
1841+
if hasattr(typing, 'Concatenate') and sys.version_info[:2] != (3, 10):
18171842
Concatenate = typing.Concatenate
1818-
_ConcatenateGenericAlias = typing._ConcatenateGenericAlias
1819-
# 3.9
1843+
# 3.9-3.10
18201844
elif sys.version_info[:2] >= (3, 9):
18211845
@_ExtensionsSpecialForm
18221846
def Concatenate(self, parameters):

0 commit comments

Comments
 (0)