From 9cc7af316cce8674f39af8959decf2ebcafdb8c4 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 8 Oct 2019 16:29:52 +0300 Subject: [PATCH] bpo-38405: Make nested subclasses of typing.NamedTuple pickleable. (GH-16641) (cherry picked from commit 13abda41003daf599587991d8291f0dacf6e9519) Co-authored-by: Serhiy Storchaka --- Lib/test/test_typing.py | 28 ++++++++++++++----- Lib/typing.py | 2 +- .../2019-10-08-11-18-40.bpo-38405.0-7e7s.rst | 1 + 3 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-10-08-11-18-40.bpo-38405.0-7e7s.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 3013f8ec41e4c7..104b7c035ca6eb 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -3468,6 +3468,9 @@ class D(UserName): class NamedTupleTests(BaseTestCase): + class NestedEmployee(NamedTuple): + name: str + cool: int def test_basics(self): Emp = NamedTuple('Emp', [('name', str), ('id', int)]) @@ -3593,14 +3596,25 @@ def test_namedtuple_errors(self): self.assertEqual(Emp.__name__, 'Emp') self.assertEqual(Emp._fields, ('name', 'id')) - def test_pickle(self): + def test_copy_and_pickle(self): global Emp # pickle wants to reference the class by name - Emp = NamedTuple('Emp', [('name', str), ('id', int)]) - jane = Emp('jane', 37) - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(jane, proto) - jane2 = pickle.loads(z) - self.assertEqual(jane2, jane) + Emp = NamedTuple('Emp', [('name', str), ('cool', int)]) + for cls in Emp, CoolEmployee, self.NestedEmployee: + with self.subTest(cls=cls): + jane = cls('jane', 37) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(jane, proto) + jane2 = pickle.loads(z) + self.assertEqual(jane2, jane) + self.assertIsInstance(jane2, cls) + + jane2 = copy(jane) + self.assertEqual(jane2, jane) + self.assertIsInstance(jane2, cls) + + jane2 = deepcopy(jane) + self.assertEqual(jane2, jane) + self.assertIsInstance(jane2, cls) class TypedDictTests(BaseTestCase): diff --git a/Lib/typing.py b/Lib/typing.py index 825be10812deef..eeebd85772788e 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1593,7 +1593,7 @@ def _make_nmtuple(name, types): '_fields', '_field_defaults', '_field_types', '_make', '_replace', '_asdict', '_source') -_special = ('__module__', '__name__', '__qualname__', '__annotations__') +_special = ('__module__', '__name__', '__annotations__') class NamedTupleMeta(type): diff --git a/Misc/NEWS.d/next/Library/2019-10-08-11-18-40.bpo-38405.0-7e7s.rst b/Misc/NEWS.d/next/Library/2019-10-08-11-18-40.bpo-38405.0-7e7s.rst new file mode 100644 index 00000000000000..ee346a30ec4165 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-10-08-11-18-40.bpo-38405.0-7e7s.rst @@ -0,0 +1 @@ +Nested subclasses of :class:`typing.NamedTuple` are now pickleable.