mirror of
https://github.com/python/cpython.git
synced 2025-07-24 11:44:31 +00:00
Issue #24164: Objects that need calling `__new__
` with keyword arguments,
can now be pickled using pickle protocols older than protocol version 4.
This commit is contained in:
parent
4e96df3b59
commit
0d554d7ef1
6 changed files with 97 additions and 32 deletions
|
@ -153,6 +153,9 @@ typedef struct {
|
|||
PyObject *codecs_encode;
|
||||
/* builtins.getattr, used for saving nested names with protocol < 4 */
|
||||
PyObject *getattr;
|
||||
/* functools.partial, used for implementing __newobj_ex__ with protocols
|
||||
2 and 3 */
|
||||
PyObject *partial;
|
||||
} PickleState;
|
||||
|
||||
/* Forward declaration of the _pickle module definition. */
|
||||
|
@ -200,6 +203,7 @@ _Pickle_InitState(PickleState *st)
|
|||
PyObject *copyreg = NULL;
|
||||
PyObject *compat_pickle = NULL;
|
||||
PyObject *codecs = NULL;
|
||||
PyObject *functools = NULL;
|
||||
|
||||
builtins = PyEval_GetBuiltins();
|
||||
if (builtins == NULL)
|
||||
|
@ -314,12 +318,21 @@ _Pickle_InitState(PickleState *st)
|
|||
}
|
||||
Py_CLEAR(codecs);
|
||||
|
||||
functools = PyImport_ImportModule("functools");
|
||||
if (!functools)
|
||||
goto error;
|
||||
st->partial = PyObject_GetAttrString(functools, "partial");
|
||||
if (!st->partial)
|
||||
goto error;
|
||||
Py_CLEAR(functools);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
Py_CLEAR(copyreg);
|
||||
Py_CLEAR(compat_pickle);
|
||||
Py_CLEAR(codecs);
|
||||
Py_CLEAR(functools);
|
||||
_Pickle_ClearState(st);
|
||||
return -1;
|
||||
}
|
||||
|
@ -3533,11 +3546,9 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
|
|||
PyErr_Clear();
|
||||
}
|
||||
else if (PyUnicode_Check(name)) {
|
||||
if (self->proto >= 4) {
|
||||
_Py_IDENTIFIER(__newobj_ex__);
|
||||
use_newobj_ex = PyUnicode_Compare(
|
||||
name, _PyUnicode_FromId(&PyId___newobj_ex__)) == 0;
|
||||
}
|
||||
_Py_IDENTIFIER(__newobj_ex__);
|
||||
use_newobj_ex = PyUnicode_Compare(
|
||||
name, _PyUnicode_FromId(&PyId___newobj_ex__)) == 0;
|
||||
if (!use_newobj_ex) {
|
||||
_Py_IDENTIFIER(__newobj__);
|
||||
use_newobj = PyUnicode_Compare(
|
||||
|
@ -3581,11 +3592,58 @@ save_reduce(PicklerObject *self, PyObject *args, PyObject *obj)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (save(self, cls, 0) < 0 ||
|
||||
save(self, args, 0) < 0 ||
|
||||
save(self, kwargs, 0) < 0 ||
|
||||
_Pickler_Write(self, &newobj_ex_op, 1) < 0) {
|
||||
return -1;
|
||||
if (self->proto >= 4) {
|
||||
if (save(self, cls, 0) < 0 ||
|
||||
save(self, args, 0) < 0 ||
|
||||
save(self, kwargs, 0) < 0 ||
|
||||
_Pickler_Write(self, &newobj_ex_op, 1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
PyObject *newargs;
|
||||
PyObject *cls_new;
|
||||
Py_ssize_t i;
|
||||
_Py_IDENTIFIER(__new__);
|
||||
|
||||
newargs = PyTuple_New(Py_SIZE(args) + 2);
|
||||
if (newargs == NULL)
|
||||
return -1;
|
||||
|
||||
cls_new = _PyObject_GetAttrId(cls, &PyId___new__);
|
||||
if (cls_new == NULL) {
|
||||
Py_DECREF(newargs);
|
||||
return -1;
|
||||
}
|
||||
PyTuple_SET_ITEM(newargs, 0, cls_new);
|
||||
Py_INCREF(cls);
|
||||
PyTuple_SET_ITEM(newargs, 1, cls);
|
||||
for (i = 0; i < Py_SIZE(args); i++) {
|
||||
PyObject *item = PyTuple_GET_ITEM(args, i);
|
||||
Py_INCREF(item);
|
||||
PyTuple_SET_ITEM(newargs, i + 2, item);
|
||||
}
|
||||
|
||||
callable = PyObject_Call(st->partial, newargs, kwargs);
|
||||
Py_DECREF(newargs);
|
||||
if (callable == NULL)
|
||||
return -1;
|
||||
|
||||
newargs = PyTuple_New(0);
|
||||
if (newargs == NULL) {
|
||||
Py_DECREF(callable);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (save(self, callable, 0) < 0 ||
|
||||
save(self, newargs, 0) < 0 ||
|
||||
_Pickler_Write(self, &reduce_op, 1) < 0) {
|
||||
Py_DECREF(newargs);
|
||||
Py_DECREF(callable);
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(newargs);
|
||||
Py_DECREF(callable);
|
||||
}
|
||||
}
|
||||
else if (use_newobj) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue