summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2015-10-10 19:42:18 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2015-10-10 19:42:18 (GMT)
commit0d554d7ef159761439ade414fcd028262eae656c (patch)
tree11aa1a1dfd3cfa07302f98d6b7b7409e9354d568 /Lib
parent4e96df3b595ed87957ee12e86e49f63b2d1cd05f (diff)
downloadcpython-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.py17
-rw-r--r--Lib/test/pickletester.py6
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),