summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2022-04-06 17:00:14 (GMT)
committerGitHub <noreply@github.com>2022-04-06 17:00:14 (GMT)
commit884eba3c76916889fd6bff3b37b8552bfb4f9566 (patch)
tree51fd55d6170cdff327ac11d70f1e5ff1aa7e735a /Objects
parentf82f9ce3239b9a7e6ffa278658dd9858f64a3c14 (diff)
downloadcpython-884eba3c76916889fd6bff3b37b8552bfb4f9566.zip
cpython-884eba3c76916889fd6bff3b37b8552bfb4f9566.tar.gz
cpython-884eba3c76916889fd6bff3b37b8552bfb4f9566.tar.bz2
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.
Diffstat (limited to 'Objects')
-rw-r--r--Objects/bytearrayobject.c29
-rw-r--r--Objects/clinic/typeobject.c.h20
-rw-r--r--Objects/odictobject.c20
-rw-r--r--Objects/setobject.c14
-rw-r--r--Objects/typeobject.c234
5 files changed, 172 insertions, 145 deletions
diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c
index 3849337..f784e04 100644
--- a/Objects/bytearrayobject.c
+++ b/Objects/bytearrayobject.c
@@ -2122,35 +2122,26 @@ bytearray_hex_impl(PyByteArrayObject *self, PyObject *sep, int bytes_per_sep)
static PyObject *
_common_reduce(PyByteArrayObject *self, int proto)
{
- PyObject *dict;
- char *buf;
+ PyObject *state;
+ const char *buf;
- if (_PyObject_LookupAttr((PyObject *)self, &_Py_ID(__dict__), &dict) < 0) {
+ state = _PyObject_GetState((PyObject *)self);
+ if (state == NULL) {
return NULL;
}
- if (dict == NULL) {
- dict = Py_None;
- Py_INCREF(dict);
- }
+ if (!Py_SIZE(self)) {
+ return Py_BuildValue("(O()N)", Py_TYPE(self), state);
+ }
buf = PyByteArray_AS_STRING(self);
if (proto < 3) {
/* use str based reduction for backwards compatibility with Python 2.x */
- PyObject *latin1;
- if (Py_SIZE(self))
- latin1 = PyUnicode_DecodeLatin1(buf, Py_SIZE(self), NULL);
- else
- latin1 = PyUnicode_FromString("");
- return Py_BuildValue("(O(Ns)N)", Py_TYPE(self), latin1, "latin-1", dict);
+ PyObject *latin1 = PyUnicode_DecodeLatin1(buf, Py_SIZE(self), NULL);
+ return Py_BuildValue("(O(Ns)N)", Py_TYPE(self), latin1, "latin-1", state);
}
else {
/* use more efficient byte based reduction */
- if (Py_SIZE(self)) {
- return Py_BuildValue("(O(y#)N)", Py_TYPE(self), buf, Py_SIZE(self), dict);
- }
- else {
- return Py_BuildValue("(O()N)", Py_TYPE(self), dict);
- }
+ return Py_BuildValue("(O(y#)N)", Py_TYPE(self), buf, Py_SIZE(self), state);
}
}
diff --git a/Objects/clinic/typeobject.c.h b/Objects/clinic/typeobject.c.h
index 8c70d76..dee3139 100644
--- a/Objects/clinic/typeobject.c.h
+++ b/Objects/clinic/typeobject.c.h
@@ -130,6 +130,24 @@ type___sizeof__(PyTypeObject *self, PyObject *Py_UNUSED(ignored))
return type___sizeof___impl(self);
}
+PyDoc_STRVAR(object___getstate____doc__,
+"__getstate__($self, /)\n"
+"--\n"
+"\n"
+"Helper for pickle.");
+
+#define OBJECT___GETSTATE___METHODDEF \
+ {"__getstate__", (PyCFunction)object___getstate__, METH_NOARGS, object___getstate____doc__},
+
+static PyObject *
+object___getstate___impl(PyObject *self);
+
+static PyObject *
+object___getstate__(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return object___getstate___impl(self);
+}
+
PyDoc_STRVAR(object___reduce____doc__,
"__reduce__($self, /)\n"
"--\n"
@@ -243,4 +261,4 @@ object___dir__(PyObject *self, PyObject *Py_UNUSED(ignored))
{
return object___dir___impl(self);
}
-/*[clinic end generated code: output=b4fb62939b08baf9 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=a30090032b8e6195 input=a9049054013a1b77]*/
diff --git a/Objects/odictobject.c b/Objects/odictobject.c
index c207593..f5b8b3e 100644
--- a/Objects/odictobject.c
+++ b/Objects/odictobject.c
@@ -947,23 +947,13 @@ PyDoc_STRVAR(odict_reduce__doc__, "Return state information for pickling");
static PyObject *
odict_reduce(register PyODictObject *od, PyObject *Py_UNUSED(ignored))
{
- PyObject *dict = NULL, *result = NULL;
+ PyObject *state, *result = NULL;
PyObject *items_iter, *items, *args = NULL;
/* capture any instance state */
- dict = PyObject_GetAttr((PyObject *)od, &_Py_ID(__dict__));
- if (dict == NULL)
+ state = _PyObject_GetState((PyObject *)od);
+ if (state == NULL)
goto Done;
- else {
- /* od.__dict__ isn't necessarily a dict... */
- Py_ssize_t dict_len = PyObject_Length(dict);
- if (dict_len == -1)
- goto Done;
- if (!dict_len) {
- /* nothing to pickle in od.__dict__ */
- Py_CLEAR(dict);
- }
- }
/* build the result */
args = PyTuple_New(0);
@@ -979,11 +969,11 @@ odict_reduce(register PyODictObject *od, PyObject *Py_UNUSED(ignored))
if (items_iter == NULL)
goto Done;
- result = PyTuple_Pack(5, Py_TYPE(od), args, dict ? dict : Py_None, Py_None, items_iter);
+ result = PyTuple_Pack(5, Py_TYPE(od), args, state, Py_None, items_iter);
Py_DECREF(items_iter);
Done:
- Py_XDECREF(dict);
+ Py_XDECREF(state);
Py_XDECREF(args);
return result;
diff --git a/Objects/setobject.c b/Objects/setobject.c
index ef2190d..4b6a8b8 100644
--- a/Objects/setobject.c
+++ b/Objects/setobject.c
@@ -1947,7 +1947,7 @@ an exception when an element is missing from the set.");
static PyObject *
set_reduce(PySetObject *so, PyObject *Py_UNUSED(ignored))
{
- PyObject *keys=NULL, *args=NULL, *result=NULL, *dict=NULL;
+ PyObject *keys=NULL, *args=NULL, *result=NULL, *state=NULL;
keys = PySequence_List((PyObject *)so);
if (keys == NULL)
@@ -1955,18 +1955,14 @@ set_reduce(PySetObject *so, PyObject *Py_UNUSED(ignored))
args = PyTuple_Pack(1, keys);
if (args == NULL)
goto done;
- if (_PyObject_LookupAttr((PyObject *)so, &_Py_ID(__dict__), &dict) < 0) {
+ state = _PyObject_GetState((PyObject *)so);
+ if (state == NULL)
goto done;
- }
- if (dict == NULL) {
- dict = Py_None;
- Py_INCREF(dict);
- }
- result = PyTuple_Pack(3, Py_TYPE(so), args, dict);
+ result = PyTuple_Pack(3, Py_TYPE(so), args, state);
done:
Py_XDECREF(args);
Py_XDECREF(keys);
- Py_XDECREF(dict);
+ Py_XDECREF(state);
return result;
}
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 5de8c3d..53e4f07 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -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 (_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;
}
- if (getstate == NULL) {
- PyObject *slotnames;
- if (required && Py_TYPE(obj)->tp_itemsize) {
+ 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 (value == NULL) {
+ Py_DECREF(name);
+ /* It is not an error if the attribute is not present. */
}
- if (Py_TYPE(obj)->tp_weaklistoffset) {
- basicsize += sizeof(PyObject *);
+ else {
+ int err = PyDict_SetItem(slots, name, value);
+ Py_DECREF(name);
+ Py_DECREF(value);
+ if (err) {
+ goto error;
+ }
}
- if (slotnames != Py_None) {
- basicsize += sizeof(PyObject *) * PyList_GET_SIZE(slotnames);
+
+ /* 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;
}
- if (Py_TYPE(obj)->tp_basicsize > basicsize) {
+
+ /* 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;
}
+ state = state2;
+ }
+ Py_DECREF(slots);
+ }
+ Py_DECREF(slotnames);
- 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;
- }
- }
+ return state;
+}
- /* If we found some slot attributes, pack them in a tuple along
- the original attribute dictionary. */
- if (PyDict_GET_SIZE(slots) > 0) {
- PyObject *state2;
+static PyObject *
+object_getstate(PyObject *obj, int required)
+{
+ PyObject *getstate, *state;
- 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);
- }
- Py_DECREF(slotnames);
+ getstate = PyObject_GetAttr(obj, &_Py_ID(__getstate__));
+ if (getstate == NULL) {
+ return NULL;
}
- else { /* getstate != 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);
- if (state == NULL)
- return NULL;
}
-
+ 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,