diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2015-10-10 19:42:18 (GMT) |
---|---|---|
committer | Serhiy Storchaka <storchaka@gmail.com> | 2015-10-10 19:42:18 (GMT) |
commit | 0d554d7ef159761439ade414fcd028262eae656c (patch) | |
tree | 11aa1a1dfd3cfa07302f98d6b7b7409e9354d568 /Lib | |
parent | 4e96df3b595ed87957ee12e86e49f63b2d1cd05f (diff) | |
download | cpython-0d554d7ef159761439ade414fcd028262eae656c.zip cpython-0d554d7ef159761439ade414fcd028262eae656c.tar.gz cpython-0d554d7ef159761439ade414fcd028262eae656c.tar.bz2 |
Issue #24164: Objects that need calling ``__new__`` with keyword arguments,
can now be pickled using pickle protocols older than protocol version 4.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/pickle.py | 17 | ||||
-rw-r--r-- | Lib/test/pickletester.py | 6 |
2 files changed, 14 insertions, 9 deletions
diff --git a/Lib/pickle.py b/Lib/pickle.py index e93057a..d41753d 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -27,6 +27,7 @@ from types import FunctionType from copyreg import dispatch_table from copyreg import _extension_registry, _inverted_registry, _extension_cache from itertools import islice +from functools import partial import sys from sys import maxsize from struct import pack, unpack @@ -544,7 +545,7 @@ class _Pickler: write = self.write func_name = getattr(func, "__name__", "") - if self.proto >= 4 and func_name == "__newobj_ex__": + if self.proto >= 2 and func_name == "__newobj_ex__": cls, args, kwargs = args if not hasattr(cls, "__new__"): raise PicklingError("args[0] from {} args has no __new__" @@ -552,10 +553,16 @@ class _Pickler: if obj is not None and cls is not obj.__class__: raise PicklingError("args[0] from {} args has the wrong class" .format(func_name)) - save(cls) - save(args) - save(kwargs) - write(NEWOBJ_EX) + if self.proto >= 4: + save(cls) + save(args) + save(kwargs) + write(NEWOBJ_EX) + else: + func = partial(cls.__new__, cls, *args, **kwargs) + save(func) + save(()) + write(REDUCE) elif self.proto >= 2 and func_name == "__newobj__": # A __reduce__ implementation can direct protocol 2 or newer to # use the more efficient NEWOBJ opcode, while still diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 2ef48e6..fd641e3 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -1580,16 +1580,14 @@ class AbstractPickleTests(unittest.TestCase): x.abc = 666 for proto in protocols: with self.subTest(proto=proto): - if 2 <= proto < 4: - self.assertRaises(ValueError, self.dumps, x, proto) - continue s = self.dumps(x, proto) if proto < 1: self.assertIn(b'\nL64206', s) # LONG elif proto < 2: self.assertIn(b'M\xce\xfa', s) # BININT2 + elif proto < 4: + self.assertIn(b'X\x04\x00\x00\x00FACE', s) # BINUNICODE else: - assert proto >= 4 self.assertIn(b'\x8c\x04FACE', s) # SHORT_BINUNICODE self.assertFalse(opcode_in_pickle(pickle.NEWOBJ, s)) self.assertEqual(opcode_in_pickle(pickle.NEWOBJ_EX, s), |