Skip to content

Commit bc310d5

Browse files
bpo-43224: Unpack concrete tuple types into a sequence of types
G[*tuple[int, str]] == G[int, str]
1 parent 5c3201e commit bc310d5

File tree

4 files changed

+32
-15
lines changed

4 files changed

+32
-15
lines changed

Lib/test/test_genericalias.py

+10-14
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,13 @@ class MyList(list):
174174
tuple[int]
175175
)
176176
]
177-
self.assertEqual(repr(x1), 'tuple[*tuple[int]]')
177+
self.assertEqual(repr(x1), 'tuple[int]')
178178
x2 = tuple[
179179
tuple( # Ditto TODO
180180
tuple[int, str]
181181
)
182182
]
183-
self.assertEqual(repr(x2), 'tuple[*tuple[int, str]]')
183+
self.assertEqual(repr(x2), 'tuple[int, str]')
184184
x3 = tuple[
185185
tuple( # Ditto TODO
186186
tuple[int, ...]
@@ -418,21 +418,17 @@ def __new__(cls, *args, **kwargs):
418418
with self.assertRaises(TypeError):
419419
Bad(list, int, bad=int)
420420

421-
def test_iter_creates_starred_tuple(self):
422-
t = tuple[int, str]
423-
iter_t = iter(t)
424-
x = next(iter_t)
425-
self.assertEqual(repr(x), '*tuple[int, str]')
421+
def test_unpack_concrete_tuple_type(self):
422+
self.assertEqual([*tuple[()]], [])
423+
self.assertEqual([*tuple[int]], [int])
424+
self.assertEqual([*tuple[int, str]], [int, str])
425+
self.assertEqual([*tuple[int, str, float]], [int, str, float])
426426

427-
def test_calling_next_twice_raises_stopiteration(self):
428-
t = tuple[int, str]
429-
iter_t = iter(t)
430-
next(iter_t)
431-
with self.assertRaises(StopIteration):
432-
next(iter_t)
427+
def test_unpack_unbounded_tuple_type(self):
428+
self.assertEqual(repr([*tuple[int, ...]]), '[*tuple[int, ...]]')
433429

434430
def test_del_iter(self):
435-
t = tuple[int, str]
431+
t = tuple[int, ...]
436432
iter_x = iter(t)
437433
del iter_x
438434

Lib/test/test_typing.py

+9
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,15 @@ def test_cannot_be_called(self):
408408
with self.assertRaises(TypeError):
409409
Unpack()
410410

411+
def test_unpack_concrete_tuple_type(self):
412+
self.assertEqual([*Tuple[()]], [])
413+
self.assertEqual([*Tuple[int]], [int])
414+
self.assertEqual([*Tuple[int, str]], [int, str])
415+
self.assertEqual([*Tuple[int, str, float]], [int, str, float])
416+
417+
def test_unpack_unbounded_tuple_type(self):
418+
self.assertEqual(repr([*Tuple[int, ...]]), '[*typing.Tuple[int, ...]]')
419+
411420

412421
class TypeVarTupleTests(BaseTestCase):
413422

Lib/typing.py

+4
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,10 @@ def __mro_entries__(self, bases):
13981398
return (self.__origin__,)
13991399

14001400
def __iter__(self):
1401+
if (self.__origin__ is tuple and
1402+
not (len(self.__args__) == 2 and self.__args__[1] is ...)):
1403+
yield from self.__args__
1404+
return
14011405
yield Unpack[self]
14021406

14031407

Objects/genericaliasobject.c

+9-1
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,15 @@ static PyTypeObject Py_GenericAliasIterType = {
667667
};
668668

669669
static PyObject *
670-
ga_iter(PyObject *self) {
670+
ga_iter(PyObject *self)
671+
{
672+
gaobject *alias = (gaobject *)self;
673+
if (alias->origin == (PyObject *)&PyTuple_Type &&
674+
!(PyTuple_GET_SIZE(alias->args) == 2 &&
675+
PyTuple_GET_ITEM(alias->args, 1) == Py_Ellipsis))
676+
{
677+
return PyObject_GetIter(alias->args);
678+
}
671679
gaiterobject *gi = PyObject_GC_New(gaiterobject, &Py_GenericAliasIterType);
672680
if (gi == NULL) {
673681
return NULL;

0 commit comments

Comments
 (0)