mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
bpo-26579: Add object.__getstate__(). (GH-2821)
Copying and pickling instances of subclasses of builtin types bytearray, set, frozenset, collections.OrderedDict, collections.deque, weakref.WeakSet, and datetime.tzinfo now copies and pickles instance attributes implemented as slots.
This commit is contained in:
parent
f82f9ce323
commit
884eba3c76
25 changed files with 389 additions and 255 deletions
|
@ -4960,143 +4960,175 @@ _PyType_GetSlotNames(PyTypeObject *cls)
|
|||
}
|
||||
|
||||
static PyObject *
|
||||
_PyObject_GetState(PyObject *obj, int required)
|
||||
object_getstate_default(PyObject *obj, int required)
|
||||
{
|
||||
PyObject *state;
|
||||
PyObject *getstate;
|
||||
PyObject *slotnames;
|
||||
|
||||
if (_PyObject_LookupAttr(obj, &_Py_ID(__getstate__), &getstate) < 0) {
|
||||
if (required && Py_TYPE(obj)->tp_itemsize) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"cannot pickle %.200s objects",
|
||||
Py_TYPE(obj)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
if (getstate == NULL) {
|
||||
PyObject *slotnames;
|
||||
|
||||
if (required && Py_TYPE(obj)->tp_itemsize) {
|
||||
if (_PyObject_IsInstanceDictEmpty(obj)) {
|
||||
state = Py_None;
|
||||
Py_INCREF(state);
|
||||
}
|
||||
else {
|
||||
state = PyObject_GenericGetDict(obj, NULL);
|
||||
if (state == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
slotnames = _PyType_GetSlotNames(Py_TYPE(obj));
|
||||
if (slotnames == NULL) {
|
||||
Py_DECREF(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(slotnames == Py_None || PyList_Check(slotnames));
|
||||
if (required) {
|
||||
Py_ssize_t basicsize = PyBaseObject_Type.tp_basicsize;
|
||||
if (Py_TYPE(obj)->tp_dictoffset &&
|
||||
(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0)
|
||||
{
|
||||
basicsize += sizeof(PyObject *);
|
||||
}
|
||||
if (Py_TYPE(obj)->tp_weaklistoffset) {
|
||||
basicsize += sizeof(PyObject *);
|
||||
}
|
||||
if (slotnames != Py_None) {
|
||||
basicsize += sizeof(PyObject *) * PyList_GET_SIZE(slotnames);
|
||||
}
|
||||
if (Py_TYPE(obj)->tp_basicsize > basicsize) {
|
||||
Py_DECREF(slotnames);
|
||||
Py_DECREF(state);
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"cannot pickle '%.200s' object",
|
||||
Py_TYPE(obj)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
if (_PyObject_IsInstanceDictEmpty(obj)) {
|
||||
state = Py_None;
|
||||
Py_INCREF(state);
|
||||
}
|
||||
else {
|
||||
state = PyObject_GenericGetDict(obj, NULL);
|
||||
if (state == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slotnames = _PyType_GetSlotNames(Py_TYPE(obj));
|
||||
if (slotnames == NULL) {
|
||||
if (slotnames != Py_None && PyList_GET_SIZE(slotnames) > 0) {
|
||||
PyObject *slots;
|
||||
Py_ssize_t slotnames_size, i;
|
||||
|
||||
slots = PyDict_New();
|
||||
if (slots == NULL) {
|
||||
Py_DECREF(slotnames);
|
||||
Py_DECREF(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(slotnames == Py_None || PyList_Check(slotnames));
|
||||
if (required) {
|
||||
Py_ssize_t basicsize = PyBaseObject_Type.tp_basicsize;
|
||||
if (Py_TYPE(obj)->tp_dictoffset &&
|
||||
(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0)
|
||||
{
|
||||
basicsize += sizeof(PyObject *);
|
||||
slotnames_size = PyList_GET_SIZE(slotnames);
|
||||
for (i = 0; i < slotnames_size; i++) {
|
||||
PyObject *name, *value;
|
||||
|
||||
name = PyList_GET_ITEM(slotnames, i);
|
||||
Py_INCREF(name);
|
||||
value = PyObject_GetAttr(obj, name);
|
||||
if (_PyObject_LookupAttr(obj, name, &value) < 0) {
|
||||
Py_DECREF(name);
|
||||
goto error;
|
||||
}
|
||||
if (Py_TYPE(obj)->tp_weaklistoffset) {
|
||||
basicsize += sizeof(PyObject *);
|
||||
if (value == NULL) {
|
||||
Py_DECREF(name);
|
||||
/* It is not an error if the attribute is not present. */
|
||||
}
|
||||
if (slotnames != Py_None) {
|
||||
basicsize += sizeof(PyObject *) * PyList_GET_SIZE(slotnames);
|
||||
else {
|
||||
int err = PyDict_SetItem(slots, name, value);
|
||||
Py_DECREF(name);
|
||||
Py_DECREF(value);
|
||||
if (err) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (Py_TYPE(obj)->tp_basicsize > basicsize) {
|
||||
|
||||
/* The list is stored on the class so it may mutate while we
|
||||
iterate over it */
|
||||
if (slotnames_size != PyList_GET_SIZE(slotnames)) {
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"__slotsname__ changed size during iteration");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* We handle errors within the loop here. */
|
||||
if (0) {
|
||||
error:
|
||||
Py_DECREF(slotnames);
|
||||
Py_DECREF(slots);
|
||||
Py_DECREF(state);
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"cannot pickle '%.200s' object",
|
||||
Py_TYPE(obj)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (slotnames != Py_None && PyList_GET_SIZE(slotnames) > 0) {
|
||||
PyObject *slots;
|
||||
Py_ssize_t slotnames_size, i;
|
||||
/* If we found some slot attributes, pack them in a tuple along
|
||||
the original attribute dictionary. */
|
||||
if (PyDict_GET_SIZE(slots) > 0) {
|
||||
PyObject *state2;
|
||||
|
||||
slots = PyDict_New();
|
||||
if (slots == NULL) {
|
||||
state2 = PyTuple_Pack(2, state, slots);
|
||||
Py_DECREF(state);
|
||||
if (state2 == NULL) {
|
||||
Py_DECREF(slotnames);
|
||||
Py_DECREF(state);
|
||||
Py_DECREF(slots);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
slotnames_size = PyList_GET_SIZE(slotnames);
|
||||
for (i = 0; i < slotnames_size; i++) {
|
||||
PyObject *name, *value;
|
||||
|
||||
name = PyList_GET_ITEM(slotnames, i);
|
||||
Py_INCREF(name);
|
||||
if (_PyObject_LookupAttr(obj, name, &value) < 0) {
|
||||
goto error;
|
||||
}
|
||||
if (value == NULL) {
|
||||
Py_DECREF(name);
|
||||
/* It is not an error if the attribute is not present. */
|
||||
}
|
||||
else {
|
||||
int err = PyDict_SetItem(slots, name, value);
|
||||
Py_DECREF(name);
|
||||
Py_DECREF(value);
|
||||
if (err) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* The list is stored on the class so it may mutate while we
|
||||
iterate over it */
|
||||
if (slotnames_size != PyList_GET_SIZE(slotnames)) {
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"__slotsname__ changed size during iteration");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* We handle errors within the loop here. */
|
||||
if (0) {
|
||||
error:
|
||||
Py_DECREF(slotnames);
|
||||
Py_DECREF(slots);
|
||||
Py_DECREF(state);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we found some slot attributes, pack them in a tuple along
|
||||
the original attribute dictionary. */
|
||||
if (PyDict_GET_SIZE(slots) > 0) {
|
||||
PyObject *state2;
|
||||
|
||||
state2 = PyTuple_Pack(2, state, slots);
|
||||
Py_DECREF(state);
|
||||
if (state2 == NULL) {
|
||||
Py_DECREF(slotnames);
|
||||
Py_DECREF(slots);
|
||||
return NULL;
|
||||
}
|
||||
state = state2;
|
||||
}
|
||||
Py_DECREF(slots);
|
||||
state = state2;
|
||||
}
|
||||
Py_DECREF(slotnames);
|
||||
}
|
||||
else { /* getstate != NULL */
|
||||
state = _PyObject_CallNoArgs(getstate);
|
||||
Py_DECREF(getstate);
|
||||
if (state == NULL)
|
||||
return NULL;
|
||||
Py_DECREF(slots);
|
||||
}
|
||||
Py_DECREF(slotnames);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
object_getstate(PyObject *obj, int required)
|
||||
{
|
||||
PyObject *getstate, *state;
|
||||
|
||||
getstate = PyObject_GetAttr(obj, &_Py_ID(__getstate__));
|
||||
if (getstate == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (PyCFunction_Check(getstate) &&
|
||||
PyCFunction_GET_SELF(getstate) == obj &&
|
||||
PyCFunction_GET_FUNCTION(getstate) == object___getstate__)
|
||||
{
|
||||
/* If __getstate__ is not overriden pass the required argument. */
|
||||
state = object_getstate_default(obj, required);
|
||||
}
|
||||
else {
|
||||
state = _PyObject_CallNoArgs(getstate);
|
||||
}
|
||||
Py_DECREF(getstate);
|
||||
return state;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyObject_GetState(PyObject *obj)
|
||||
{
|
||||
return object_getstate(obj, 0);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
object.__getstate__
|
||||
|
||||
Helper for pickle.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
object___getstate___impl(PyObject *self)
|
||||
/*[clinic end generated code: output=5a2500dcb6217e9e input=692314d8fbe194ee]*/
|
||||
{
|
||||
return object_getstate_default(self, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
_PyObject_GetNewArguments(PyObject *obj, PyObject **args, PyObject **kwargs)
|
||||
{
|
||||
|
@ -5309,8 +5341,7 @@ reduce_newobj(PyObject *obj)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
state = _PyObject_GetState(obj,
|
||||
!hasargs && !PyList_Check(obj) && !PyDict_Check(obj));
|
||||
state = object_getstate(obj, !(hasargs || PyList_Check(obj) || PyDict_Check(obj)));
|
||||
if (state == NULL) {
|
||||
Py_DECREF(newobj);
|
||||
Py_DECREF(newargs);
|
||||
|
@ -5558,6 +5589,7 @@ error:
|
|||
static PyMethodDef object_methods[] = {
|
||||
OBJECT___REDUCE_EX___METHODDEF
|
||||
OBJECT___REDUCE___METHODDEF
|
||||
OBJECT___GETSTATE___METHODDEF
|
||||
{"__subclasshook__", object_subclasshook, METH_CLASS | METH_VARARGS,
|
||||
object_subclasshook_doc},
|
||||
{"__init_subclass__", object_init_subclass, METH_CLASS | METH_NOARGS,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue