From bc310d50a6a80e7911d1a744db776d8299aa6ce5 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 21 Mar 2022 18:11:53 +0200 Subject: [PATCH] bpo-43224: Unpack concrete tuple types into a sequence of types G[*tuple[int, str]] == G[int, str] --- Lib/test/test_genericalias.py | 24 ++++++++++-------------- Lib/test/test_typing.py | 9 +++++++++ Lib/typing.py | 4 ++++ Objects/genericaliasobject.c | 10 +++++++++- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py index 39c56f2290bd35..03b82a907a71fd 100644 --- a/Lib/test/test_genericalias.py +++ b/Lib/test/test_genericalias.py @@ -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, ...] @@ -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 diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 0e28655296d14f..830b4144c47f8e 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -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): diff --git a/Lib/typing.py b/Lib/typing.py index f0e84900d7f80a..1d7e467c13e2ab 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -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] diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 224a2e9acb748e..32bc3c3a906d8b 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -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;