Skip to content

Commit 4f4f2c4

Browse files
committed
add __reduce__ and __reduce_ex__ from PyPy
1 parent 1e379e1 commit 4f4f2c4

File tree

2 files changed

+145
-35
lines changed

2 files changed

+145
-35
lines changed

graalpython/lib-graalpython/object.py

Lines changed: 144 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,147 @@
1-
# Copyright (c) 2018, Oracle and/or its affiliates.
1+
# Copyright (c) 2017, 2018, Oracle and/or its affiliates.
2+
# Copyright (c) 2017, The PyPy Project
23
#
3-
# The Universal Permissive License (UPL), Version 1.0
4+
# The MIT License
5+
# Permission is hereby granted, free of charge, to any person
6+
# obtaining a copy of this software and associated documentation
7+
# files (the "Software"), to deal in the Software without
8+
# restriction, including without limitation the rights to use,
9+
# copy, modify, merge, publish, distribute, sublicense, and/or
10+
# sell copies of the Software, and to permit persons to whom the
11+
# Software is furnished to do so, subject to the following conditions:
412
#
5-
# Subject to the condition set forth below, permission is hereby granted to any
6-
# person obtaining a copy of this software, associated documentation and/or data
7-
# (collectively the "Software"), free of charge and under any and all copyright
8-
# rights in the Software, and any and all patent rights owned or freely
9-
# licensable by each licensor hereunder covering either (i) the unmodified
10-
# Software as contributed to or provided by such licensor, or (ii) the Larger
11-
# Works (as defined below), to deal in both
13+
# The above copyright notice and this permission notice shall be included
14+
# in all copies or substantial portions of the Software.
1215
#
13-
# (a) the Software, and
14-
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
15-
# one is included with the Software (each a "Larger Work" to which the
16-
# Software is contributed by such licensors),
17-
#
18-
# without restriction, including without limitation the rights to copy, create
19-
# derivative works of, display, perform, and distribute the Software and make,
20-
# use, sell, offer for sale, import, export, have made, and have sold the
21-
# Software and the Larger Work(s), and to sublicense the foregoing rights on
22-
# either these or other terms.
23-
#
24-
# This license is subject to the following condition:
25-
#
26-
# The above copyright notice and either this complete permission notice or at a
27-
# minimum a reference to the UPL must be included in all copies or substantial
28-
# portions of the Software.
29-
#
30-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36-
# SOFTWARE.
37-
38-
# an empty file
16+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22+
# DEALINGS IN THE SOFTWARE.
23+
24+
def __reduce__(obj, proto=0):
25+
if proto >= 2:
26+
descr = getattr(w_obj, '__getnewargs_ex__', None)
27+
hasargs = True
28+
if descr is not None:
29+
result = descr()
30+
if not isinstance(result, tuple):
31+
raise TypeError("__getnewargs_ex__ should return a tuple, not '%s'", type(result))
32+
n = len(result)
33+
if n != 2:
34+
raise ValueError("__getnewargs_ex__ should return a tuple of length 2, not %d", n)
35+
args, kwargs = result
36+
if isinstance(args, tuple):
37+
raise TypeError("first item of the tuple returned by __getnewargs_ex__ must be a tuple, not '%s'", type(args))
38+
if not isinstance(kwargs, dict):
39+
raise TypeError("second item of the tuple returned by __getnewargs_ex__ must be a dict, not '%s'", type(kwargs))
40+
else:
41+
descr = getattr(obj, '__getnewargs__', None)
42+
if descr is not None:
43+
args = descr(obj)
44+
if not isinstance(args, tuple):
45+
raise TypeError("__getnewargs__ should return a tuple, not '%s'", type(args))
46+
else:
47+
hasargs = False
48+
args = tuple()
49+
kwargs = None
50+
getstate = getattr(obj, '__get_state__')
51+
if getstate is None:
52+
required = (not hasargs and
53+
not isinstance(obj, list) and
54+
not isinstance(obj, dict))
55+
obj_type = type(obj)
56+
if required:
57+
raise TypeError("cannot pickle %s objects", obj_type)
58+
return reduce_2(obj, proto, args, kwargs)
59+
return reduce_1(obj, proto)
60+
61+
62+
def _getstate(obj):
63+
cls = obj.__class__
64+
65+
try:
66+
getstate = obj.__getstate__
67+
except AttributeError:
68+
# and raises a TypeError if the condition holds true, this is done
69+
# just before reduce_2 is called in pypy
70+
state = getattr(obj, "__dict__", None)
71+
# CPython returns None if the dict is empty
72+
if state is not None and len(state) == 0:
73+
state = None
74+
names = slotnames(cls) # not checking for list
75+
if names is not None:
76+
slots = {}
77+
for name in names:
78+
try:
79+
value = getattr(obj, name)
80+
except AttributeError:
81+
pass
82+
else:
83+
slots[name] = value
84+
if slots:
85+
state = state, slots
86+
else:
87+
state = getstate()
88+
return state
89+
90+
91+
def reduce_2(obj, proto, args, kwargs):
92+
cls = obj.__class__
93+
94+
if not hasattr(type(obj), "__new__"):
95+
raise TypeError("can't pickle %s objects" % type(obj).__name__)
96+
97+
import copyreg
98+
99+
if not isinstance(args, tuple):
100+
raise TypeError("__getnewargs__ should return a tuple")
101+
if not kwargs:
102+
newobj = copyreg.__newobj__
103+
args2 = (cls,) + args
104+
elif proto >= 4:
105+
newobj = copyreg.__newobj_ex__
106+
args2 = (cls, args, kwargs)
107+
else:
108+
raise ValueError("must use protocol 4 or greater to copy this "
109+
"object; since __getnewargs_ex__ returned "
110+
"keyword arguments.")
111+
state = _getstate(obj)
112+
listitems = iter(obj) if isinstance(obj, list) else None
113+
dictitems = iter(obj.items()) if isinstance(obj, dict) else None
114+
115+
return newobj, args2, state, listitems, dictitems
116+
117+
118+
def slotnames(cls):
119+
if not isinstance(cls, type):
120+
return None
121+
122+
try:
123+
return cls.__dict__["__slotnames__"]
124+
except KeyError:
125+
pass
126+
127+
import copyreg
128+
slotnames = copyreg._slotnames(cls)
129+
if not isinstance(slotnames, list) and slotnames is not None:
130+
raise TypeError("copyreg._slotnames didn't return a list or None")
131+
return slotnames
132+
133+
134+
def __reduce_ex__(obj, proto=0):
135+
obj_reduce = getattr(obj, "__reduce__", None)
136+
if obj_reduce is not None:
137+
# Check if __reduce__ has been overridden:
138+
# "type(obj).__reduce__ is not object.__reduce__"
139+
cls_reduce = getattr(type(obj), "__reduce__", None)
140+
override = cls_reduce is not obj_reduce
141+
if override:
142+
return obj_reduce()
143+
return __reduce__(obj, proto)
144+
145+
146+
object.__reduce__ = __reduce__
147+
object.__reduce_ex__ = __reduce_ex__

mx.graalpython/copyrights/overrides

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@ graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence
534534
graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/TypedSequenceStorage.java,zippy.copyright
535535
graalpython/lib-graalpython/_io.py,pypy.copyright
536536
graalpython/lib-graalpython/itertools.py,pypy.copyright
537+
graalpython/lib-graalpython/object.py,pypy.copyright
537538
graalpython/lib-graalpython/str.py,pypy.copyright
538539
graalpython/lib-graalpython/timsort.py,pypy.copyright
539540
mx.graalpython/mx_graalpython.py,zippy.copyright

0 commit comments

Comments
 (0)