Skip to content

Commit

Permalink
bpo-43224: Unpack concrete tuple types into a sequence of types
Browse files Browse the repository at this point in the history
G[*tuple[int, str]] == G[int, str]
  • Loading branch information
serhiy-storchaka committed Mar 21, 2022
1 parent 5c3201e commit bc310d5
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 15 deletions.
24 changes: 10 additions & 14 deletions Lib/test/test_genericalias.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,13 @@ class MyList(list):
tuple[int]
)
]
self.assertEqual(repr(x1), 'tuple[*tuple[int]]')
self.assertEqual(repr(x1), 'tuple[int]')
x2 = tuple[
tuple( # Ditto TODO
tuple[int, str]
)
]
self.assertEqual(repr(x2), 'tuple[*tuple[int, str]]')
self.assertEqual(repr(x2), 'tuple[int, str]')
x3 = tuple[
tuple( # Ditto TODO
tuple[int, ...]
Expand Down Expand Up @@ -418,21 +418,17 @@ def __new__(cls, *args, **kwargs):
with self.assertRaises(TypeError):
Bad(list, int, bad=int)

def test_iter_creates_starred_tuple(self):
t = tuple[int, str]
iter_t = iter(t)
x = next(iter_t)
self.assertEqual(repr(x), '*tuple[int, str]')
def test_unpack_concrete_tuple_type(self):
self.assertEqual([*tuple[()]], [])
self.assertEqual([*tuple[int]], [int])
self.assertEqual([*tuple[int, str]], [int, str])
self.assertEqual([*tuple[int, str, float]], [int, str, float])

def test_calling_next_twice_raises_stopiteration(self):
t = tuple[int, str]
iter_t = iter(t)
next(iter_t)
with self.assertRaises(StopIteration):
next(iter_t)
def test_unpack_unbounded_tuple_type(self):
self.assertEqual(repr([*tuple[int, ...]]), '[*tuple[int, ...]]')

def test_del_iter(self):
t = tuple[int, str]
t = tuple[int, ...]
iter_x = iter(t)
del iter_x

Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,15 @@ def test_cannot_be_called(self):
with self.assertRaises(TypeError):
Unpack()

def test_unpack_concrete_tuple_type(self):
self.assertEqual([*Tuple[()]], [])
self.assertEqual([*Tuple[int]], [int])
self.assertEqual([*Tuple[int, str]], [int, str])
self.assertEqual([*Tuple[int, str, float]], [int, str, float])

def test_unpack_unbounded_tuple_type(self):
self.assertEqual(repr([*Tuple[int, ...]]), '[*typing.Tuple[int, ...]]')


class TypeVarTupleTests(BaseTestCase):

Expand Down
4 changes: 4 additions & 0 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1398,6 +1398,10 @@ def __mro_entries__(self, bases):
return (self.__origin__,)

def __iter__(self):
if (self.__origin__ is tuple and
not (len(self.__args__) == 2 and self.__args__[1] is ...)):
yield from self.__args__
return
yield Unpack[self]


Expand Down
10 changes: 9 additions & 1 deletion Objects/genericaliasobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,15 @@ static PyTypeObject Py_GenericAliasIterType = {
};

static PyObject *
ga_iter(PyObject *self) {
ga_iter(PyObject *self)
{
gaobject *alias = (gaobject *)self;
if (alias->origin == (PyObject *)&PyTuple_Type &&
!(PyTuple_GET_SIZE(alias->args) == 2 &&
PyTuple_GET_ITEM(alias->args, 1) == Py_Ellipsis))
{
return PyObject_GetIter(alias->args);
}
gaiterobject *gi = PyObject_GC_New(gaiterobject, &Py_GenericAliasIterType);
if (gi == NULL) {
return NULL;
Expand Down

0 comments on commit bc310d5

Please sign in to comment.