Skip to content

Commit 205fd42

Browse files
committed
GR-27400: IntBuiltins add missing __getnewargs__ magic method
- fix __reduce__ and __reduce_ex__ object.py builtin
1 parent a8d1549 commit 205fd42

File tree

2 files changed

+89
-82
lines changed

2 files changed

+89
-82
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2792,6 +2792,25 @@ static PythonNativeVoidPtr doL(PythonNativeVoidPtr self) {
27922792
abstract static class IndexNode extends IntNode {
27932793
}
27942794

2795+
@Builtin(name = SpecialMethodNames.__GETNEWARGS__, minNumOfPositionalArgs = 1)
2796+
@GenerateNodeFactory
2797+
abstract static class GetNewArgsNode extends PythonUnaryBuiltinNode {
2798+
@Specialization
2799+
Object doI(int self) {
2800+
return factory().createTuple(new Object[]{factory().createInt(self)});
2801+
}
2802+
2803+
@Specialization
2804+
Object doL(long self) {
2805+
return factory().createTuple(new Object[]{factory().createInt(self)});
2806+
}
2807+
2808+
@Specialization
2809+
Object getPI(PInt self) {
2810+
return factory().createTuple(new Object[]{factory().createInt(self.getValue())});
2811+
}
2812+
}
2813+
27952814
@Builtin(name = SpecialMethodNames.__FLOAT__, minNumOfPositionalArgs = 1)
27962815
@GenerateNodeFactory
27972816
@TypeSystemReference(PythonArithmeticTypes.class)

graalpython/lib-graalpython/object.py

Lines changed: 70 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -21,56 +21,77 @@
2121
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2222
# DEALINGS IN THE SOFTWARE.
2323

24+
copyreg = None
25+
26+
27+
def import_copyreg():
28+
global copyreg
29+
if not copyreg:
30+
import copyreg
31+
32+
2433
def __reduce__(obj, proto=0):
2534
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, '__getstate__')
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__
35+
return reduce_newobj(obj)
36+
37+
proto = int(proto)
38+
import_copyreg()
39+
return copyreg._reduce_ex(obj, proto)
40+
41+
42+
def _get_new_arguments(obj):
43+
# We first attempt to fetch the arguments for __new__ by calling __getnewargs_ex__ on the object.
44+
getnewargs_ex = getattr(obj, '__getnewargs_ex__', None)
45+
if getnewargs_ex is not None:
46+
newargs = getnewargs_ex()
47+
if not isinstance(newargs, tuple):
48+
raise TypeError("__getnewargs_ex__ should return a tuple, not '{}'".format(type(newargs)))
49+
if len(newargs) != 2:
50+
raise ValueError("__getnewargs_ex__ should return a tuple of length 2, not {}".format(len(newargs)))
51+
args, kwargs = newargs
52+
if not isinstance(args, tuple):
53+
raise TypeError("first item of the tuple returned by __getnewargs_ex__ must be a tuple, not '{}".format(type(args)))
54+
if not isinstance(kwargs, dict):
55+
raise TypeError("second item of the tuple returned by __getnewargs_ex__ must be a dict, not '{}'".format(type(kwargs)))
56+
return args, kwargs
57+
58+
getnewargs = getattr(obj, '__getnewargs__', None)
59+
if getnewargs is not None:
60+
args = getnewargs()
61+
if not isinstance(args, tuple):
62+
raise TypeError("__getnewargs__ should return a tuple, not '{}'".format(type(args)))
63+
return args, None
64+
65+
# The object does not have __getnewargs_ex__ and __getnewargs__. This may
66+
# mean __new__ does not takes any arguments on this object, or that the
67+
# object does not implement the reduce protocol for pickling or
68+
# copying.
69+
return None, None
70+
71+
72+
def reduce_newobj(obj):
73+
cls = type(obj)
74+
args, kwargs = _get_new_arguments(obj)
75+
import_copyreg()
76+
77+
hasargs = args is not None
78+
if kwargs is None or len(kwargs) == 0:
79+
newobj = copyreg.__newobj__
80+
newargs = (cls, ) + args if args else tuple()
81+
elif args is not None:
82+
newobj = copyreg.__newobj_ex__
83+
newargs = (cls, args, kwargs)
84+
else:
85+
import sys
86+
frame = sys._getframe(0)
87+
file_name = frame.f_code.co_filename
88+
line_no = frame.f_lineno
89+
raise SystemError("{}:{}: bad argument to internal function".format(file_name, line_no))
6490

6591
try:
6692
getstate = obj.__getstate__
6793
except AttributeError:
68-
# and raises a TypeError if the condition holds true, this is done
69-
# just before reduce_2 is called in pypy
7094
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
7495
names = slotnames(cls) # not checking for list
7596
if names is not None:
7697
slots = {}
@@ -85,44 +106,10 @@ def _getstate(obj):
85106
state = state, slots
86107
else:
87108
state = getstate()
88-
return state
89-
90-
91-
copyreg = None
92-
def reduce_1(obj, proto):
93-
global copyreg
94-
if not copyreg:
95-
import copyreg
96-
return copyreg._reduce_ex(obj, proto)
97-
98-
99-
def reduce_2(obj, proto, args, kwargs):
100-
cls = obj.__class__
101-
102-
if not hasattr(type(obj), "__new__"):
103-
raise TypeError("can't pickle %s objects" % type(obj).__name__)
104-
105-
global copyreg
106-
if not copyreg:
107-
import copyreg
108-
109-
if not isinstance(args, tuple):
110-
raise TypeError("__getnewargs__ should return a tuple")
111-
if not kwargs:
112-
newobj = copyreg.__newobj__
113-
args2 = (cls,) + args
114-
elif proto >= 4:
115-
newobj = copyreg.__newobj_ex__
116-
args2 = (cls, args, kwargs)
117-
else:
118-
raise ValueError("must use protocol 4 or greater to copy this "
119-
"object; since __getnewargs_ex__ returned "
120-
"keyword arguments.")
121-
state = _getstate(obj)
122109
listitems = iter(obj) if isinstance(obj, list) else None
123-
dictitems = iter(obj.items()) if isinstance(obj, dict) else None
110+
dictitems = obj.iteritems() if isinstance(obj, dict) else None
124111

125-
return newobj, args2, state, listitems, dictitems
112+
return newobj, newargs, state, listitems, dictitems
126113

127114

128115
def slotnames(cls):
@@ -144,14 +131,15 @@ def slotnames(cls):
144131

145132

146133
def __reduce_ex__(obj, proto=0):
147-
obj_reduce = getattr(obj, "__reduce__", None)
148-
if obj_reduce is not None:
134+
obj_reduce = getattr(type, "__reduce__", None)
135+
_reduce = getattr(obj, "__reduce__", None)
136+
if _reduce is not None:
149137
# Check if __reduce__ has been overridden:
150138
# "type(obj).__reduce__ is not object.__reduce__"
151139
cls_reduce = getattr(type(obj), "__reduce__", None)
152140
override = cls_reduce is not obj_reduce
153141
if override:
154-
return obj_reduce()
142+
return _reduce()
155143
return __reduce__(obj, proto)
156144

157145

0 commit comments

Comments
 (0)