[pypy-commit] pypy py3.5: Pickle protocol 4: Call __getnewargs_ex__(), implemented when __new__() requires keyword arguments.
amauryfa
pypy.commits at gmail.com
Wed Nov 2 04:05:28 EDT 2016
Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: py3.5
Changeset: r88056:82efc04d5dda
Date: 2016-11-02 09:04 +0100
http://bitbucket.org/pypy/pypy/changeset/82efc04d5dda/
Log: Pickle protocol 4: Call __getnewargs_ex__(), implemented when
__new__() requires keyword arguments.
diff --git a/pypy/objspace/std/objectobject.py b/pypy/objspace/std/objectobject.py
--- a/pypy/objspace/std/objectobject.py
+++ b/pypy/objspace/std/objectobject.py
@@ -18,17 +18,27 @@
import copyreg
return copyreg._reduce_ex(obj, proto)
-def reduce_2(obj):
- cls = obj.__class__
+def _getnewargs(obj):
try:
- getnewargs = obj.__getnewargs__
+ getnewargs = obj.__getnewargs_ex__
except AttributeError:
- args = ()
+ try:
+ getnewargs = obj.__getnewargs__
+ except AttributeError:
+ args = ()
+ else:
+ args = getnewargs()
+ kwargs = None
else:
- args = getnewargs()
- if not isinstance(args, tuple):
- raise TypeError("__getnewargs__ should return a tuple")
+ args, kwargs = getnewargs()
+
+ if not isinstance(args, tuple):
+ raise TypeError("__getnewargs__ should return a tuple")
+ return args, kwargs
+
+def _getstate(obj):
+ cls = obj.__class__
try:
getstate = obj.__getstate__
@@ -48,15 +58,32 @@
state = state, slots
else:
state = getstate()
+ return state
+def reduce_2(obj, proto):
+ cls = obj.__class__
+
+ import copyreg
+
+ args, kwargs = _getnewargs(obj)
+
+ if not kwargs:
+ newobj = copyreg.__newobj__
+ args2 = (cls,) + args
+ elif proto >= 4:
+ newobj = copyreg.__newobj_ex__
+ args2 = (cls, args, kwargs)
+ else:
+ raise ValueError("must use protocol 4 or greater to copy this "
+ "object; since __getnewargs_ex__ returned "
+ "keyword arguments.")
+
+ state = _getstate(obj)
listitems = iter(obj) if isinstance(obj, list) else None
dictitems = iter(obj.items()) if isinstance(obj, dict) else None
- import copyreg
- newobj = copyreg.__newobj__
+ return newobj, args2, state, listitems, dictitems
- args2 = (cls,) + args
- return newobj, args2, state, listitems, dictitems
def slotnames(cls):
if not isinstance(cls, type):
@@ -169,9 +196,9 @@
@unwrap_spec(proto=int)
def descr__reduce__(space, w_obj, proto=0):
+ w_proto = space.wrap(proto)
if proto >= 2:
- return reduce_2(space, w_obj)
- w_proto = space.wrap(proto)
+ return reduce_2(space, w_obj, w_proto)
return reduce_1(space, w_obj, w_proto)
@unwrap_spec(proto=int)
diff --git a/pypy/objspace/std/test/test_obj.py b/pypy/objspace/std/test/test_obj.py
--- a/pypy/objspace/std/test/test_obj.py
+++ b/pypy/objspace/std/test/test_obj.py
@@ -59,6 +59,22 @@
s = X().__reduce__()
assert s[-1] == ':-)'
+ def test_getnewargs_ex(self):
+ class NamedInt(int):
+ def __new__(cls, name, **kwargs):
+ if len(kwargs) == 0:
+ raise TypeError("name and value must be specified")
+ self = int.__new__(cls, kwargs['value'])
+ self._name = name
+ return self
+ def __getnewargs_ex__(self):
+ return (self._name,), dict(value=int(self))
+ import copyreg
+ assert NamedInt("Name", value=42).__reduce__(4) == (
+ copyreg.__newobj_ex__,
+ (NamedInt, ('Name',), dict(value=42)),
+ dict(_name='Name'), None, None)
+
def test_default_format(self):
class x(object):
def __str__(self):
More information about the pypy-commit
mailing list