mirror of https://github.com/python/cpython
Issue #9935: Speed up pickling of instances of user-defined classes.
This commit is contained in:
parent
f5f6288bbc
commit
16c4ce1903
|
@ -3,6 +3,7 @@ import unittest
|
||||||
import pickle
|
import pickle
|
||||||
import pickletools
|
import pickletools
|
||||||
import copyreg
|
import copyreg
|
||||||
|
import weakref
|
||||||
from http.cookies import SimpleCookie
|
from http.cookies import SimpleCookie
|
||||||
|
|
||||||
from test.support import TestFailed, TESTFN, run_with_locale, no_tracing
|
from test.support import TestFailed, TESTFN, run_with_locale, no_tracing
|
||||||
|
@ -842,6 +843,25 @@ class AbstractPickleTests(unittest.TestCase):
|
||||||
self.assertEqual(B(x), B(y), detail)
|
self.assertEqual(B(x), B(y), detail)
|
||||||
self.assertEqual(x.__dict__, y.__dict__, detail)
|
self.assertEqual(x.__dict__, y.__dict__, detail)
|
||||||
|
|
||||||
|
def test_newobj_proxies(self):
|
||||||
|
# NEWOBJ should use the __class__ rather than the raw type
|
||||||
|
classes = myclasses[:]
|
||||||
|
# Cannot create weakproxies to these classes
|
||||||
|
for c in (MyInt, MyTuple):
|
||||||
|
classes.remove(c)
|
||||||
|
for proto in protocols:
|
||||||
|
for C in classes:
|
||||||
|
B = C.__base__
|
||||||
|
x = C(C.sample)
|
||||||
|
x.foo = 42
|
||||||
|
p = weakref.proxy(x)
|
||||||
|
s = self.dumps(p, proto)
|
||||||
|
y = self.loads(s)
|
||||||
|
self.assertEqual(type(y), type(x)) # rather than type(p)
|
||||||
|
detail = (proto, C, B, x, y, type(y))
|
||||||
|
self.assertEqual(B(x), B(y), detail)
|
||||||
|
self.assertEqual(x.__dict__, y.__dict__, detail)
|
||||||
|
|
||||||
# Register a type with copyreg, with extension code extcode. Pickle
|
# Register a type with copyreg, with extension code extcode. Pickle
|
||||||
# an object of that type. Check that the resulting pickle uses opcode
|
# an object of that type. Check that the resulting pickle uses opcode
|
||||||
# (EXT[124]) under proto 2, and not in proto 1.
|
# (EXT[124]) under proto 2, and not in proto 1.
|
||||||
|
@ -1009,7 +1029,6 @@ class AbstractPickleTests(unittest.TestCase):
|
||||||
self.assertRaises(RuntimeError, self.dumps, x, proto)
|
self.assertRaises(RuntimeError, self.dumps, x, proto)
|
||||||
# protocol 2 don't raise a RuntimeError.
|
# protocol 2 don't raise a RuntimeError.
|
||||||
d = self.dumps(x, 2)
|
d = self.dumps(x, 2)
|
||||||
self.assertRaises(RuntimeError, self.loads, d)
|
|
||||||
|
|
||||||
def test_reduce_bad_iterator(self):
|
def test_reduce_bad_iterator(self):
|
||||||
# Issue4176: crash when 4th and 5th items of __reduce__()
|
# Issue4176: crash when 4th and 5th items of __reduce__()
|
||||||
|
|
|
@ -62,6 +62,8 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #9935: Speed up pickling of instances of user-defined classes.
|
||||||
|
|
||||||
- Issue #5622: Fix curses.wrapper to raise correct exception if curses
|
- Issue #5622: Fix curses.wrapper to raise correct exception if curses
|
||||||
initialization fails.
|
initialization fails.
|
||||||
|
|
||||||
|
|
|
@ -2842,6 +2842,28 @@ save_pers(PicklerObject *self, PyObject *obj, PyObject *func)
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
get_class(PyObject *obj)
|
||||||
|
{
|
||||||
|
PyObject *cls;
|
||||||
|
static PyObject *str_class;
|
||||||
|
|
||||||
|
if (str_class == NULL) {
|
||||||
|
str_class = PyUnicode_InternFromString("__class__");
|
||||||
|
if (str_class == NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cls = PyObject_GetAttr(obj, str_class);
|
||||||
|
if (cls == NULL) {
|
||||||
|
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||||||
|
PyErr_Clear();
|
||||||
|
cls = (PyObject *) Py_TYPE(obj);
|
||||||
|
Py_INCREF(cls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
/* We're saving obj, and args is the 2-thru-5 tuple returned by the
|
/* We're saving obj, and args is the 2-thru-5 tuple returned by the
|
||||||
* appropriate __reduce__ method for obj.
|
* appropriate __reduce__ method for obj.
|
||||||
*/
|
*/
|
||||||
|
@ -2907,17 +2929,18 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
|
||||||
/* Protocol 2 special case: if callable's name is __newobj__, use
|
/* Protocol 2 special case: if callable's name is __newobj__, use
|
||||||
NEWOBJ. */
|
NEWOBJ. */
|
||||||
if (use_newobj) {
|
if (use_newobj) {
|
||||||
static PyObject *newobj_str = NULL;
|
static PyObject *newobj_str = NULL, *name_str = NULL;
|
||||||
PyObject *name_str;
|
PyObject *name;
|
||||||
|
|
||||||
if (newobj_str == NULL) {
|
if (newobj_str == NULL) {
|
||||||
newobj_str = PyUnicode_InternFromString("__newobj__");
|
newobj_str = PyUnicode_InternFromString("__newobj__");
|
||||||
if (newobj_str == NULL)
|
name_str = PyUnicode_InternFromString("__name__");
|
||||||
|
if (newobj_str == NULL || name_str == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
name_str = PyObject_GetAttrString(callable, "__name__");
|
name = PyObject_GetAttr(callable, name_str);
|
||||||
if (name_str == NULL) {
|
if (name == NULL) {
|
||||||
if (PyErr_ExceptionMatches(PyExc_AttributeError))
|
if (PyErr_ExceptionMatches(PyExc_AttributeError))
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
else
|
else
|
||||||
|
@ -2925,9 +2948,9 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
|
||||||
use_newobj = 0;
|
use_newobj = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
use_newobj = PyUnicode_Check(name_str) &&
|
use_newobj = PyUnicode_Check(name) &&
|
||||||
PyUnicode_Compare(name_str, newobj_str) == 0;
|
PyUnicode_Compare(name, newobj_str) == 0;
|
||||||
Py_DECREF(name_str);
|
Py_DECREF(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (use_newobj) {
|
if (use_newobj) {
|
||||||
|
@ -2943,20 +2966,14 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
cls = PyTuple_GET_ITEM(argtup, 0);
|
cls = PyTuple_GET_ITEM(argtup, 0);
|
||||||
if (!PyObject_HasAttrString(cls, "__new__")) {
|
if (!PyType_Check(cls)) {
|
||||||
PyErr_SetString(PicklingError, "args[0] from "
|
PyErr_SetString(PicklingError, "args[0] from "
|
||||||
"__newobj__ args has no __new__");
|
"__newobj__ args is not a type");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj != NULL) {
|
if (obj != NULL) {
|
||||||
obj_class = PyObject_GetAttrString(obj, "__class__");
|
obj_class = get_class(obj);
|
||||||
if (obj_class == NULL) {
|
|
||||||
if (PyErr_ExceptionMatches(PyExc_AttributeError))
|
|
||||||
PyErr_Clear();
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p = obj_class != cls; /* true iff a problem */
|
p = obj_class != cls; /* true iff a problem */
|
||||||
Py_DECREF(obj_class);
|
Py_DECREF(obj_class);
|
||||||
if (p) {
|
if (p) {
|
||||||
|
|
|
@ -3073,14 +3073,19 @@ static PyObject *
|
||||||
import_copyreg(void)
|
import_copyreg(void)
|
||||||
{
|
{
|
||||||
static PyObject *copyreg_str;
|
static PyObject *copyreg_str;
|
||||||
|
static PyObject *mod_copyreg = NULL;
|
||||||
|
|
||||||
if (!copyreg_str) {
|
if (!copyreg_str) {
|
||||||
copyreg_str = PyUnicode_InternFromString("copyreg");
|
copyreg_str = PyUnicode_InternFromString("copyreg");
|
||||||
if (copyreg_str == NULL)
|
if (copyreg_str == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (!mod_copyreg) {
|
||||||
|
mod_copyreg = PyImport_Import(copyreg_str);
|
||||||
|
}
|
||||||
|
|
||||||
return PyImport_Import(copyreg_str);
|
Py_XINCREF(mod_copyreg);
|
||||||
|
return mod_copyreg;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -3089,14 +3094,16 @@ slotnames(PyObject *cls)
|
||||||
PyObject *clsdict;
|
PyObject *clsdict;
|
||||||
PyObject *copyreg;
|
PyObject *copyreg;
|
||||||
PyObject *slotnames;
|
PyObject *slotnames;
|
||||||
|
static PyObject *str_slotnames;
|
||||||
|
|
||||||
if (!PyType_Check(cls)) {
|
if (str_slotnames == NULL) {
|
||||||
Py_INCREF(Py_None);
|
str_slotnames = PyUnicode_InternFromString("__slotnames__");
|
||||||
return Py_None;
|
if (str_slotnames == NULL)
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
clsdict = ((PyTypeObject *)cls)->tp_dict;
|
clsdict = ((PyTypeObject *)cls)->tp_dict;
|
||||||
slotnames = PyDict_GetItemString(clsdict, "__slotnames__");
|
slotnames = PyDict_GetItem(clsdict, str_slotnames);
|
||||||
if (slotnames != NULL && PyList_Check(slotnames)) {
|
if (slotnames != NULL && PyList_Check(slotnames)) {
|
||||||
Py_INCREF(slotnames);
|
Py_INCREF(slotnames);
|
||||||
return slotnames;
|
return slotnames;
|
||||||
|
@ -3130,12 +3137,20 @@ reduce_2(PyObject *obj)
|
||||||
PyObject *slots = NULL, *listitems = NULL, *dictitems = NULL;
|
PyObject *slots = NULL, *listitems = NULL, *dictitems = NULL;
|
||||||
PyObject *copyreg = NULL, *newobj = NULL, *res = NULL;
|
PyObject *copyreg = NULL, *newobj = NULL, *res = NULL;
|
||||||
Py_ssize_t i, n;
|
Py_ssize_t i, n;
|
||||||
|
static PyObject *str_getnewargs = NULL, *str_getstate = NULL,
|
||||||
|
*str_newobj = NULL;
|
||||||
|
|
||||||
cls = PyObject_GetAttrString(obj, "__class__");
|
if (str_getnewargs == NULL) {
|
||||||
if (cls == NULL)
|
str_getnewargs = PyUnicode_InternFromString("__getnewargs__");
|
||||||
return NULL;
|
str_getstate = PyUnicode_InternFromString("__getstate__");
|
||||||
|
str_newobj = PyUnicode_InternFromString("__newobj__");
|
||||||
|
if (!str_getnewargs || !str_getstate || !str_newobj)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
getnewargs = PyObject_GetAttrString(obj, "__getnewargs__");
|
cls = (PyObject *) Py_TYPE(obj);
|
||||||
|
|
||||||
|
getnewargs = PyObject_GetAttr(obj, str_getnewargs);
|
||||||
if (getnewargs != NULL) {
|
if (getnewargs != NULL) {
|
||||||
args = PyObject_CallObject(getnewargs, NULL);
|
args = PyObject_CallObject(getnewargs, NULL);
|
||||||
Py_DECREF(getnewargs);
|
Py_DECREF(getnewargs);
|
||||||
|
@ -3153,7 +3168,7 @@ reduce_2(PyObject *obj)
|
||||||
if (args == NULL)
|
if (args == NULL)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
getstate = PyObject_GetAttrString(obj, "__getstate__");
|
getstate = PyObject_GetAttr(obj, str_getstate);
|
||||||
if (getstate != NULL) {
|
if (getstate != NULL) {
|
||||||
state = PyObject_CallObject(getstate, NULL);
|
state = PyObject_CallObject(getstate, NULL);
|
||||||
Py_DECREF(getstate);
|
Py_DECREF(getstate);
|
||||||
|
@ -3161,17 +3176,18 @@ reduce_2(PyObject *obj)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
PyObject **dict;
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
state = PyObject_GetAttrString(obj, "__dict__");
|
dict = _PyObject_GetDictPtr(obj);
|
||||||
if (state == NULL) {
|
if (dict && *dict)
|
||||||
PyErr_Clear();
|
state = *dict;
|
||||||
|
else
|
||||||
state = Py_None;
|
state = Py_None;
|
||||||
Py_INCREF(state);
|
Py_INCREF(state);
|
||||||
}
|
|
||||||
names = slotnames(cls);
|
names = slotnames(cls);
|
||||||
if (names == NULL)
|
if (names == NULL)
|
||||||
goto end;
|
goto end;
|
||||||
if (names != Py_None) {
|
if (names != Py_None && PyList_GET_SIZE(names) > 0) {
|
||||||
assert(PyList_Check(names));
|
assert(PyList_Check(names));
|
||||||
slots = PyDict_New();
|
slots = PyDict_New();
|
||||||
if (slots == NULL)
|
if (slots == NULL)
|
||||||
|
@ -3230,7 +3246,7 @@ reduce_2(PyObject *obj)
|
||||||
copyreg = import_copyreg();
|
copyreg = import_copyreg();
|
||||||
if (copyreg == NULL)
|
if (copyreg == NULL)
|
||||||
goto end;
|
goto end;
|
||||||
newobj = PyObject_GetAttrString(copyreg, "__newobj__");
|
newobj = PyObject_GetAttr(copyreg, str_newobj);
|
||||||
if (newobj == NULL)
|
if (newobj == NULL)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
|
@ -3238,8 +3254,8 @@ reduce_2(PyObject *obj)
|
||||||
args2 = PyTuple_New(n+1);
|
args2 = PyTuple_New(n+1);
|
||||||
if (args2 == NULL)
|
if (args2 == NULL)
|
||||||
goto end;
|
goto end;
|
||||||
|
Py_INCREF(cls);
|
||||||
PyTuple_SET_ITEM(args2, 0, cls);
|
PyTuple_SET_ITEM(args2, 0, cls);
|
||||||
cls = NULL;
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
PyObject *v = PyTuple_GET_ITEM(args, i);
|
PyObject *v = PyTuple_GET_ITEM(args, i);
|
||||||
Py_INCREF(v);
|
Py_INCREF(v);
|
||||||
|
@ -3249,7 +3265,6 @@ reduce_2(PyObject *obj)
|
||||||
res = PyTuple_Pack(5, newobj, args2, state, listitems, dictitems);
|
res = PyTuple_Pack(5, newobj, args2, state, listitems, dictitems);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
Py_XDECREF(cls);
|
|
||||||
Py_XDECREF(args);
|
Py_XDECREF(args);
|
||||||
Py_XDECREF(args2);
|
Py_XDECREF(args2);
|
||||||
Py_XDECREF(slots);
|
Py_XDECREF(slots);
|
||||||
|
@ -3309,31 +3324,34 @@ object_reduce(PyObject *self, PyObject *args)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
object_reduce_ex(PyObject *self, PyObject *args)
|
object_reduce_ex(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
|
static PyObject *str_reduce = NULL, *objreduce;
|
||||||
PyObject *reduce, *res;
|
PyObject *reduce, *res;
|
||||||
int proto = 0;
|
int proto = 0;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "|i:__reduce_ex__", &proto))
|
if (!PyArg_ParseTuple(args, "|i:__reduce_ex__", &proto))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
reduce = PyObject_GetAttrString(self, "__reduce__");
|
if (str_reduce == NULL) {
|
||||||
|
str_reduce = PyUnicode_InternFromString("__reduce__");
|
||||||
|
objreduce = PyDict_GetItemString(PyBaseObject_Type.tp_dict,
|
||||||
|
"__reduce__");
|
||||||
|
if (str_reduce == NULL || objreduce == NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
reduce = PyObject_GetAttr(self, str_reduce);
|
||||||
if (reduce == NULL)
|
if (reduce == NULL)
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
else {
|
else {
|
||||||
PyObject *cls, *clsreduce, *objreduce;
|
PyObject *cls, *clsreduce;
|
||||||
int override;
|
int override;
|
||||||
cls = PyObject_GetAttrString(self, "__class__");
|
|
||||||
if (cls == NULL) {
|
cls = (PyObject *) Py_TYPE(self);
|
||||||
Py_DECREF(reduce);
|
clsreduce = PyObject_GetAttr(cls, str_reduce);
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
clsreduce = PyObject_GetAttrString(cls, "__reduce__");
|
|
||||||
Py_DECREF(cls);
|
|
||||||
if (clsreduce == NULL) {
|
if (clsreduce == NULL) {
|
||||||
Py_DECREF(reduce);
|
Py_DECREF(reduce);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
objreduce = PyDict_GetItemString(PyBaseObject_Type.tp_dict,
|
|
||||||
"__reduce__");
|
|
||||||
override = (clsreduce != objreduce);
|
override = (clsreduce != objreduce);
|
||||||
Py_DECREF(clsreduce);
|
Py_DECREF(clsreduce);
|
||||||
if (override) {
|
if (override) {
|
||||||
|
|
Loading…
Reference in New Issue