Skip to content

Commit 1eabcb6

Browse files
committed
Full backport to 3.8 to support Concatenate[...]
1 parent 3112a80 commit 1eabcb6

File tree

2 files changed

+46
-60
lines changed

2 files changed

+46
-60
lines changed

src/test_typing_extensions.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,8 +1727,6 @@ class C(Generic[T]): pass
17271727
self.assertEqual(get_args(Callable[Concatenate[int, P], int]),
17281728
(Concatenate[int, P], int))
17291729
with self.subTest("Concatenate[int, ...]"):
1730-
if sys.version_info < (3, 9, 2):
1731-
self.skipTest("arguments must be types before 3.9.2, i.e. no ...")
17321730
self.assertEqual(get_args(Callable[Concatenate[int, ...], int]),
17331731
(Concatenate[int, ...], int))
17341732

@@ -5385,19 +5383,16 @@ def test_valid_uses(self):
53855383
self.assertEqual(C1.__origin__, C2.__origin__)
53865384
self.assertNotEqual(C1, C2)
53875385

5388-
@skipUnless(TYPING_3_9_0, "Needs PEP 585; no backport for 3.8 typing")
5389-
def test_valid_uses_py39_plus(self):
5390-
P = ParamSpec('P')
5391-
T = TypeVar('T')
5392-
53935386
with self.subTest("typing.Callable with Ellipsis"):
5394-
if sys.version_info < (3, 9, 2):
5395-
self.skipTest("Must use types before 3.9.2")
53965387
C3 = Callable[Concatenate[int, ...], int]
53975388
C4 = Callable[Concatenate[int, T, ...], T]
53985389
self.assertEqual(C3.__origin__, C4.__origin__)
53995390
self.assertNotEqual(C3, C4)
54005391

5392+
@skipUnless(TYPING_3_9_0, "Needs PEP 585")
5393+
def test_pep585_collections_callable(self):
5394+
P = ParamSpec('P')
5395+
T = TypeVar('T')
54015396
# Test collections.abc.Callable too.
54025397
C5 = collections.abc.Callable[Concatenate[int, P], int]
54035398
C6 = collections.abc.Callable[Concatenate[int, T, P], T]
@@ -5425,14 +5420,11 @@ def test_invalid_uses(self):
54255420
):
54265421
Concatenate[P, T]
54275422

5428-
@skipIf(sys.version_info < (3, 9, 2), "Args must be types below 3.9.2")
5429-
def test_invalid_uses_py39_2_plus(self):
5430-
T = TypeVar('T')
54315423
with self.assertRaisesRegex(
54325424
TypeError,
54335425
'is not a generic class',
54345426
):
5435-
Callable[Concatenate[int, ...], Any][T]
5427+
Callable[Concatenate[int, ...], Any][Any]
54365428

54375429
@skipIf(TYPING_3_11_0, "Args can be non-types in 3.11+")
54385430
def test_invalid_uses_before_3_11(self):
@@ -5450,15 +5442,15 @@ def test_invalid_uses_before_3_11(self):
54505442
Concatenate[1, ..., P]
54515443

54525444
@skipUnless(TYPING_3_11_0 or (3, 10, 0) <= sys.version_info < (3, 10, 2),
5453-
"Cannot be backported to <=3.9"
5445+
"Cannot be backported to <=3.9. See issue #48"
54545446
"Cannot use ... with typing._ConcatenateGenericAlias after 3.10.2")
54555447
def test_alias_subscription_with_ellipsis(self):
54565448
P = ParamSpec('P')
54575449
X = Callable[Concatenate[int, P], Any]
54585450

54595451
C1 = X[...]
54605452
self.assertEqual(C1.__parameters__, ())
5461-
self.assertEqual(C1.__args__, (Concatenate[int, ...], Any))
5453+
self.assertEqual(get_args(C1), (Concatenate[int, ...], Any))
54625454

54635455
def test_basic_introspection(self):
54645456
P = ParamSpec('P')

src/typing_extensions.py

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,53 +1799,47 @@ def __parameters__(self):
17991799
else:
18001800
_ConcatenateGenericAlias = typing._ConcatenateGenericAlias
18011801

1802-
# 3.10.2+
1803-
if sys.version_info >= (3, 10, 2):
1804-
_ellipsis_dummy = ParamSpec('_ellipsis_dummy')
1805-
1806-
@typing._tp_cache
1807-
def _concatenate_getitem(self, parameters):
1808-
if parameters == ():
1809-
raise TypeError("Cannot take a Concatenate of no types.")
1810-
if not isinstance(parameters, tuple):
1811-
parameters = (parameters,)
1812-
if not (parameters[-1] is ... or isinstance(parameters[-1], ParamSpec)):
1813-
raise TypeError("The last parameter to Concatenate should be a "
1814-
"ParamSpec variable or ellipsis.")
1815-
msg = "Concatenate[arg, ...]: each arg must be a type."
1816-
parameters = (*(typing._type_check(p, msg) for p in parameters[:-1]),
1817-
parameters[-1])
1818-
if parameters[-1] is Ellipsis:
1819-
# Hack: Need ParamSpec as last parameter when passing to typing class in 3.10
1820-
parameters = parameters[:-1] + (_ellipsis_dummy,)
1821-
concatenate = _ConcatenateGenericAlias(self, parameters,
1822-
_typevar_types=(TypeVar, ParamSpec),
1823-
_paramspec_tvars=True)
1824-
# Remove dummy and replace with Ellipsis again
1825-
concatenate.__args__ = tuple(p if p is not _ellipsis_dummy else ...
1826-
for p in concatenate.__args__)
1827-
concatenate.__parameters__ = tuple(p for p in concatenate.__parameters__
1828-
if p is not _ellipsis_dummy)
1829-
return concatenate
1830-
return _ConcatenateGenericAlias(self, parameters,
1802+
# 3.8-3.9,2
1803+
class _EllipsisDummyType: ...
1804+
1805+
# 3.8-3.10
1806+
def _create_concatenate_alias(origin, parameters):
1807+
if parameters[-1] is ... and sys.version_info < (3, 9, 2):
1808+
# Arguments must be types
1809+
parameters = parameters[:-1] + (_EllipsisDummyType,)
1810+
if sys.version_info >= (3, 10, 2):
1811+
concatenate = _ConcatenateGenericAlias(origin, parameters,
18311812
_typevar_types=(TypeVar, ParamSpec),
18321813
_paramspec_tvars=True)
1833-
1834-
# 3.8-3.10.0
1835-
else:
1836-
@typing._tp_cache
1837-
def _concatenate_getitem(self, parameters):
1838-
if parameters == ():
1839-
raise TypeError("Cannot take a Concatenate of no types.")
1840-
if not isinstance(parameters, tuple):
1841-
parameters = (parameters,)
1842-
if not (parameters[-1] is ... or isinstance(parameters[-1], ParamSpec)):
1843-
raise TypeError("The last parameter to Concatenate should be a "
1844-
"ParamSpec variable or ellipsis.")
1845-
msg = "Concatenate[arg, ...]: each arg must be a type."
1846-
parameters = (*(typing._type_check(p, msg) for p in parameters[:-1]),
1847-
parameters[-1])
1848-
return _ConcatenateGenericAlias(self, parameters)
1814+
else:
1815+
concatenate = _ConcatenateGenericAlias(origin, parameters)
1816+
if parameters[-1] is not _EllipsisDummyType:
1817+
return concatenate
1818+
# Remove dummy again
1819+
concatenate.__args__ = tuple(p if p is not _EllipsisDummyType else ...
1820+
for p in concatenate.__args__)
1821+
if sys.version_info < (3, 10):
1822+
# backport needs __args__ adjustment only
1823+
return concatenate
1824+
concatenate.__parameters__ = tuple(p for p in concatenate.__parameters__
1825+
if p is not _EllipsisDummyType)
1826+
return concatenate
1827+
1828+
1829+
# 3.8-3.10
1830+
@typing._tp_cache
1831+
def _concatenate_getitem(self, parameters):
1832+
if parameters == ():
1833+
raise TypeError("Cannot take a Concatenate of no types.")
1834+
if not isinstance(parameters, tuple):
1835+
parameters = (parameters,)
1836+
if not (parameters[-1] is ... or isinstance(parameters[-1], ParamSpec)):
1837+
raise TypeError("The last parameter to Concatenate should be a "
1838+
"ParamSpec variable or ellipsis.")
1839+
msg = "Concatenate[arg, ...]: each arg must be a type."
1840+
parameters = (*(typing._type_check(p, msg) for p in parameters[:-1]),
1841+
parameters[-1])
1842+
return _create_concatenate_alias(self, parameters)
18491843

18501844
# 3.11+; Concatenate does not accept ellipsis in 3.10
18511845
if hasattr(typing, 'Concatenate') and sys.version_info >= (3, 11):

0 commit comments

Comments
 (0)