mirror of https://github.com/python/cpython
Issue #22783: Pickling now uses the NEWOBJ opcode instead of the NEWOBJ_EX
opcode if possible.
This commit is contained in:
parent
01bdd9a980
commit
707b5ccde5
|
@ -1153,16 +1153,62 @@ class AbstractPickleTests(unittest.TestCase):
|
||||||
self.assertGreaterEqual(num_additems, 2)
|
self.assertGreaterEqual(num_additems, 2)
|
||||||
|
|
||||||
def test_simple_newobj(self):
|
def test_simple_newobj(self):
|
||||||
x = object.__new__(SimpleNewObj) # avoid __init__
|
x = SimpleNewObj.__new__(SimpleNewObj, 0xface) # avoid __init__
|
||||||
x.abc = 666
|
x.abc = 666
|
||||||
for proto in protocols:
|
for proto in protocols:
|
||||||
s = self.dumps(x, proto)
|
with self.subTest(proto=proto):
|
||||||
self.assertEqual(opcode_in_pickle(pickle.NEWOBJ, s),
|
s = self.dumps(x, proto)
|
||||||
2 <= proto < 4)
|
if proto < 1:
|
||||||
self.assertEqual(opcode_in_pickle(pickle.NEWOBJ_EX, s),
|
self.assertIn(b'\nL64206', s) # LONG
|
||||||
proto >= 4)
|
else:
|
||||||
y = self.loads(s) # will raise TypeError if __init__ called
|
self.assertIn(b'M\xce\xfa', s) # BININT2
|
||||||
self.assert_is_copy(x, y)
|
self.assertEqual(opcode_in_pickle(pickle.NEWOBJ, s),
|
||||||
|
2 <= proto)
|
||||||
|
self.assertFalse(opcode_in_pickle(pickle.NEWOBJ_EX, s))
|
||||||
|
y = self.loads(s) # will raise TypeError if __init__ called
|
||||||
|
self.assert_is_copy(x, y)
|
||||||
|
|
||||||
|
def test_complex_newobj(self):
|
||||||
|
x = ComplexNewObj.__new__(ComplexNewObj, 0xface) # avoid __init__
|
||||||
|
x.abc = 666
|
||||||
|
for proto in protocols:
|
||||||
|
with self.subTest(proto=proto):
|
||||||
|
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:
|
||||||
|
self.assertIn(b'\x8c\x04FACE', s) # SHORT_BINUNICODE
|
||||||
|
self.assertEqual(opcode_in_pickle(pickle.NEWOBJ, s),
|
||||||
|
2 <= proto)
|
||||||
|
self.assertFalse(opcode_in_pickle(pickle.NEWOBJ_EX, s))
|
||||||
|
y = self.loads(s) # will raise TypeError if __init__ called
|
||||||
|
self.assert_is_copy(x, y)
|
||||||
|
|
||||||
|
def test_complex_newobj_ex(self):
|
||||||
|
x = ComplexNewObjEx.__new__(ComplexNewObjEx, 0xface) # avoid __init__
|
||||||
|
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
|
||||||
|
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),
|
||||||
|
4 <= proto)
|
||||||
|
y = self.loads(s) # will raise TypeError if __init__ called
|
||||||
|
self.assert_is_copy(x, y)
|
||||||
|
|
||||||
def test_newobj_list_slots(self):
|
def test_newobj_list_slots(self):
|
||||||
x = SlotList([1, 2, 3])
|
x = SlotList([1, 2, 3])
|
||||||
|
@ -1891,12 +1937,20 @@ myclasses = [MyInt, MyFloat,
|
||||||
class SlotList(MyList):
|
class SlotList(MyList):
|
||||||
__slots__ = ["foo"]
|
__slots__ = ["foo"]
|
||||||
|
|
||||||
class SimpleNewObj(object):
|
class SimpleNewObj(int):
|
||||||
def __init__(self, a, b, c):
|
def __init__(self, *args, **kwargs):
|
||||||
# raise an error, to make sure this isn't called
|
# raise an error, to make sure this isn't called
|
||||||
raise TypeError("SimpleNewObj.__init__() didn't expect to get called")
|
raise TypeError("SimpleNewObj.__init__() didn't expect to get called")
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.__dict__ == other.__dict__
|
return int(self) == int(other) and self.__dict__ == other.__dict__
|
||||||
|
|
||||||
|
class ComplexNewObj(SimpleNewObj):
|
||||||
|
def __getnewargs__(self):
|
||||||
|
return ('%X' % self, 16)
|
||||||
|
|
||||||
|
class ComplexNewObjEx(SimpleNewObj):
|
||||||
|
def __getnewargs_ex__(self):
|
||||||
|
return ('%X' % self,), {'base': 16}
|
||||||
|
|
||||||
class BadGetattr:
|
class BadGetattr:
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
|
|
|
@ -4592,26 +4592,15 @@ class PicklingTests(unittest.TestCase):
|
||||||
|
|
||||||
def _check_reduce(self, proto, obj, args=(), kwargs={}, state=None,
|
def _check_reduce(self, proto, obj, args=(), kwargs={}, state=None,
|
||||||
listitems=None, dictitems=None):
|
listitems=None, dictitems=None):
|
||||||
if proto >= 4:
|
if proto >= 2:
|
||||||
reduce_value = obj.__reduce_ex__(proto)
|
reduce_value = obj.__reduce_ex__(proto)
|
||||||
self.assertEqual(reduce_value[:3],
|
if kwargs:
|
||||||
(copyreg.__newobj_ex__,
|
self.assertEqual(reduce_value[0], copyreg.__newobj_ex__)
|
||||||
(type(obj), args, kwargs),
|
self.assertEqual(reduce_value[1], (type(obj), args, kwargs))
|
||||||
state))
|
|
||||||
if listitems is not None:
|
|
||||||
self.assertListEqual(list(reduce_value[3]), listitems)
|
|
||||||
else:
|
else:
|
||||||
self.assertIsNone(reduce_value[3])
|
self.assertEqual(reduce_value[0], copyreg.__newobj__)
|
||||||
if dictitems is not None:
|
self.assertEqual(reduce_value[1], (type(obj),) + args)
|
||||||
self.assertDictEqual(dict(reduce_value[4]), dictitems)
|
self.assertEqual(reduce_value[2], state)
|
||||||
else:
|
|
||||||
self.assertIsNone(reduce_value[4])
|
|
||||||
elif proto >= 2:
|
|
||||||
reduce_value = obj.__reduce_ex__(proto)
|
|
||||||
self.assertEqual(reduce_value[:3],
|
|
||||||
(copyreg.__newobj__,
|
|
||||||
(type(obj),) + args,
|
|
||||||
state))
|
|
||||||
if listitems is not None:
|
if listitems is not None:
|
||||||
self.assertListEqual(list(reduce_value[3]), listitems)
|
self.assertListEqual(list(reduce_value[3]), listitems)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -196,6 +196,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #22783: Pickling now uses the NEWOBJ opcode instead of the NEWOBJ_EX
|
||||||
|
opcode if possible.
|
||||||
|
|
||||||
- Issue #15513: Added a __sizeof__ implementation for pickle classes.
|
- Issue #15513: Added a __sizeof__ implementation for pickle classes.
|
||||||
|
|
||||||
- Issue #19858: pickletools.optimize() now aware of the MEMOIZE opcode, can
|
- Issue #19858: pickletools.optimize() now aware of the MEMOIZE opcode, can
|
||||||
|
|
|
@ -3506,20 +3506,19 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
|
||||||
}
|
}
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
else if (self->proto >= 4) {
|
else if (PyUnicode_Check(name)) {
|
||||||
_Py_IDENTIFIER(__newobj_ex__);
|
if (self->proto >= 4) {
|
||||||
use_newobj_ex = PyUnicode_Check(name) &&
|
_Py_IDENTIFIER(__newobj_ex__);
|
||||||
PyUnicode_Compare(
|
use_newobj_ex = PyUnicode_Compare(
|
||||||
name, _PyUnicode_FromId(&PyId___newobj_ex__)) == 0;
|
name, _PyUnicode_FromId(&PyId___newobj_ex__)) == 0;
|
||||||
Py_DECREF(name);
|
}
|
||||||
}
|
if (!use_newobj_ex) {
|
||||||
else {
|
_Py_IDENTIFIER(__newobj__);
|
||||||
_Py_IDENTIFIER(__newobj__);
|
use_newobj = PyUnicode_Compare(
|
||||||
use_newobj = PyUnicode_Check(name) &&
|
name, _PyUnicode_FromId(&PyId___newobj__)) == 0;
|
||||||
PyUnicode_Compare(
|
}
|
||||||
name, _PyUnicode_FromId(&PyId___newobj__)) == 0;
|
|
||||||
Py_DECREF(name);
|
|
||||||
}
|
}
|
||||||
|
Py_XDECREF(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_newobj_ex) {
|
if (use_newobj_ex) {
|
||||||
|
|
|
@ -3886,48 +3886,87 @@ _PyObject_GetItemsIter(PyObject *obj, PyObject **listitems,
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
reduce_4(PyObject *obj)
|
reduce_newobj(PyObject *obj, int proto)
|
||||||
{
|
{
|
||||||
PyObject *args = NULL, *kwargs = NULL;
|
PyObject *args = NULL, *kwargs = NULL;
|
||||||
PyObject *copyreg;
|
PyObject *copyreg;
|
||||||
PyObject *newobj, *newargs, *state, *listitems, *dictitems;
|
PyObject *newobj, *newargs, *state, *listitems, *dictitems;
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
_Py_IDENTIFIER(__newobj_ex__);
|
|
||||||
|
|
||||||
if (_PyObject_GetNewArguments(obj, &args, &kwargs) < 0) {
|
if (_PyObject_GetNewArguments(obj, &args, &kwargs) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
if (args == NULL) {
|
if (args == NULL) {
|
||||||
args = PyTuple_New(0);
|
args = PyTuple_New(0);
|
||||||
if (args == NULL)
|
if (args == NULL) {
|
||||||
|
Py_XDECREF(kwargs);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (kwargs == NULL) {
|
|
||||||
kwargs = PyDict_New();
|
|
||||||
if (kwargs == NULL)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
copyreg = import_copyreg();
|
copyreg = import_copyreg();
|
||||||
if (copyreg == NULL) {
|
if (copyreg == NULL) {
|
||||||
Py_DECREF(args);
|
Py_DECREF(args);
|
||||||
Py_DECREF(kwargs);
|
Py_XDECREF(kwargs);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
newobj = _PyObject_GetAttrId(copyreg, &PyId___newobj_ex__);
|
if (kwargs == NULL || PyDict_Size(kwargs) == 0) {
|
||||||
Py_DECREF(copyreg);
|
_Py_IDENTIFIER(__newobj__);
|
||||||
if (newobj == NULL) {
|
PyObject *cls;
|
||||||
|
Py_ssize_t i, n;
|
||||||
|
|
||||||
|
Py_XDECREF(kwargs);
|
||||||
|
newobj = _PyObject_GetAttrId(copyreg, &PyId___newobj__);
|
||||||
|
Py_DECREF(copyreg);
|
||||||
|
if (newobj == NULL) {
|
||||||
|
Py_DECREF(args);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
n = PyTuple_GET_SIZE(args);
|
||||||
|
newargs = PyTuple_New(n+1);
|
||||||
|
if (newargs == NULL) {
|
||||||
|
Py_DECREF(args);
|
||||||
|
Py_DECREF(newobj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cls = (PyObject *) Py_TYPE(obj);
|
||||||
|
Py_INCREF(cls);
|
||||||
|
PyTuple_SET_ITEM(newargs, 0, cls);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
PyObject *v = PyTuple_GET_ITEM(args, i);
|
||||||
|
Py_INCREF(v);
|
||||||
|
PyTuple_SET_ITEM(newargs, i+1, v);
|
||||||
|
}
|
||||||
|
Py_DECREF(args);
|
||||||
|
}
|
||||||
|
else if (proto >= 4) {
|
||||||
|
_Py_IDENTIFIER(__newobj_ex__);
|
||||||
|
|
||||||
|
newobj = _PyObject_GetAttrId(copyreg, &PyId___newobj_ex__);
|
||||||
|
Py_DECREF(copyreg);
|
||||||
|
if (newobj == NULL) {
|
||||||
|
Py_DECREF(args);
|
||||||
|
Py_DECREF(kwargs);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
newargs = PyTuple_Pack(3, Py_TYPE(obj), args, kwargs);
|
||||||
Py_DECREF(args);
|
Py_DECREF(args);
|
||||||
Py_DECREF(kwargs);
|
Py_DECREF(kwargs);
|
||||||
|
if (newargs == NULL) {
|
||||||
|
Py_DECREF(newobj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"must use protocol 4 or greater to copy this "
|
||||||
|
"object; since __getnewargs_ex__ returned "
|
||||||
|
"keyword arguments.");
|
||||||
|
Py_DECREF(args);
|
||||||
|
Py_DECREF(kwargs);
|
||||||
|
Py_DECREF(copyreg);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
newargs = PyTuple_Pack(3, Py_TYPE(obj), args, kwargs);
|
|
||||||
Py_DECREF(args);
|
|
||||||
Py_DECREF(kwargs);
|
|
||||||
if (newargs == NULL) {
|
|
||||||
Py_DECREF(newobj);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
state = _PyObject_GetState(obj);
|
state = _PyObject_GetState(obj);
|
||||||
if (state == NULL) {
|
if (state == NULL) {
|
||||||
Py_DECREF(newobj);
|
Py_DECREF(newobj);
|
||||||
|
@ -3950,79 +3989,6 @@ reduce_4(PyObject *obj)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
reduce_2(PyObject *obj)
|
|
||||||
{
|
|
||||||
PyObject *cls;
|
|
||||||
PyObject *args = NULL, *args2 = NULL, *kwargs = NULL;
|
|
||||||
PyObject *state = NULL, *listitems = NULL, *dictitems = NULL;
|
|
||||||
PyObject *copyreg = NULL, *newobj = NULL, *res = NULL;
|
|
||||||
Py_ssize_t i, n;
|
|
||||||
_Py_IDENTIFIER(__newobj__);
|
|
||||||
|
|
||||||
if (_PyObject_GetNewArguments(obj, &args, &kwargs) < 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (args == NULL) {
|
|
||||||
assert(kwargs == NULL);
|
|
||||||
args = PyTuple_New(0);
|
|
||||||
if (args == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (kwargs != NULL) {
|
|
||||||
if (PyDict_Size(kwargs) > 0) {
|
|
||||||
PyErr_SetString(PyExc_ValueError,
|
|
||||||
"must use protocol 4 or greater to copy this "
|
|
||||||
"object; since __getnewargs_ex__ returned "
|
|
||||||
"keyword arguments.");
|
|
||||||
Py_DECREF(args);
|
|
||||||
Py_DECREF(kwargs);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
Py_CLEAR(kwargs);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = _PyObject_GetState(obj);
|
|
||||||
if (state == NULL)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
if (_PyObject_GetItemsIter(obj, &listitems, &dictitems) < 0)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
copyreg = import_copyreg();
|
|
||||||
if (copyreg == NULL)
|
|
||||||
goto end;
|
|
||||||
newobj = _PyObject_GetAttrId(copyreg, &PyId___newobj__);
|
|
||||||
if (newobj == NULL)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
n = PyTuple_GET_SIZE(args);
|
|
||||||
args2 = PyTuple_New(n+1);
|
|
||||||
if (args2 == NULL)
|
|
||||||
goto end;
|
|
||||||
cls = (PyObject *) Py_TYPE(obj);
|
|
||||||
Py_INCREF(cls);
|
|
||||||
PyTuple_SET_ITEM(args2, 0, cls);
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
PyObject *v = PyTuple_GET_ITEM(args, i);
|
|
||||||
Py_INCREF(v);
|
|
||||||
PyTuple_SET_ITEM(args2, i+1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = PyTuple_Pack(5, newobj, args2, state, listitems, dictitems);
|
|
||||||
|
|
||||||
end:
|
|
||||||
Py_XDECREF(args);
|
|
||||||
Py_XDECREF(args2);
|
|
||||||
Py_XDECREF(state);
|
|
||||||
Py_XDECREF(listitems);
|
|
||||||
Py_XDECREF(dictitems);
|
|
||||||
Py_XDECREF(copyreg);
|
|
||||||
Py_XDECREF(newobj);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There were two problems when object.__reduce__ and object.__reduce_ex__
|
* There were two problems when object.__reduce__ and object.__reduce_ex__
|
||||||
* were implemented in the same function:
|
* were implemented in the same function:
|
||||||
|
@ -4043,10 +4009,8 @@ _common_reduce(PyObject *self, int proto)
|
||||||
{
|
{
|
||||||
PyObject *copyreg, *res;
|
PyObject *copyreg, *res;
|
||||||
|
|
||||||
if (proto >= 4)
|
if (proto >= 2)
|
||||||
return reduce_4(self);
|
return reduce_newobj(self, proto);
|
||||||
else if (proto >= 2)
|
|
||||||
return reduce_2(self);
|
|
||||||
|
|
||||||
copyreg = import_copyreg();
|
copyreg = import_copyreg();
|
||||||
if (!copyreg)
|
if (!copyreg)
|
||||||
|
|
Loading…
Reference in New Issue