mirror of
https://github.com/python/cpython.git
synced 2025-12-04 00:30:19 +00:00
Issue #22955: attrgetter, itemgetter and methodcaller objects in the operator
module now support pickling. Added readable and evaluable repr for these objects. Based on patch by Josh Rosenberg.
This commit is contained in:
parent
5418d0bfc4
commit
35ac5f8280
4 changed files with 427 additions and 9 deletions
|
|
@ -485,6 +485,41 @@ itemgetter_call(itemgetterobject *ig, PyObject *args, PyObject *kw)
|
|||
return result;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
itemgetter_repr(itemgetterobject *ig)
|
||||
{
|
||||
PyObject *repr;
|
||||
const char *reprfmt;
|
||||
|
||||
int status = Py_ReprEnter((PyObject *)ig);
|
||||
if (status != 0) {
|
||||
if (status < 0)
|
||||
return NULL;
|
||||
return PyUnicode_FromFormat("%s(...)", Py_TYPE(ig)->tp_name);
|
||||
}
|
||||
|
||||
reprfmt = ig->nitems == 1 ? "%s(%R)" : "%s%R";
|
||||
repr = PyUnicode_FromFormat(reprfmt, Py_TYPE(ig)->tp_name, ig->item);
|
||||
Py_ReprLeave((PyObject *)ig);
|
||||
return repr;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
itemgetter_reduce(itemgetterobject *ig)
|
||||
{
|
||||
if (ig->nitems == 1)
|
||||
return Py_BuildValue("O(O)", Py_TYPE(ig), ig->item);
|
||||
return PyTuple_Pack(2, Py_TYPE(ig), ig->item);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(reduce_doc, "Return state information for pickling");
|
||||
|
||||
static PyMethodDef itemgetter_methods[] = {
|
||||
{"__reduce__", (PyCFunction)itemgetter_reduce, METH_NOARGS,
|
||||
reduce_doc},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(itemgetter_doc,
|
||||
"itemgetter(item, ...) --> itemgetter object\n\
|
||||
\n\
|
||||
|
|
@ -503,7 +538,7 @@ static PyTypeObject itemgetter_type = {
|
|||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_reserved */
|
||||
0, /* tp_repr */
|
||||
(reprfunc)itemgetter_repr, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
|
|
@ -521,7 +556,7 @@ static PyTypeObject itemgetter_type = {
|
|||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
itemgetter_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
|
|
@ -737,6 +772,91 @@ attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw)
|
|||
return result;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
dotjoinattr(PyObject *attr, PyObject **attrsep)
|
||||
{
|
||||
if (PyTuple_CheckExact(attr)) {
|
||||
if (*attrsep == NULL) {
|
||||
*attrsep = PyUnicode_FromString(".");
|
||||
if (*attrsep == NULL)
|
||||
return NULL;
|
||||
}
|
||||
return PyUnicode_Join(*attrsep, attr);
|
||||
} else {
|
||||
Py_INCREF(attr);
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
attrgetter_args(attrgetterobject *ag)
|
||||
{
|
||||
Py_ssize_t i;
|
||||
PyObject *attrsep = NULL;
|
||||
PyObject *attrstrings = PyTuple_New(ag->nattrs);
|
||||
if (attrstrings == NULL)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < ag->nattrs; ++i) {
|
||||
PyObject *attr = PyTuple_GET_ITEM(ag->attr, i);
|
||||
PyObject *attrstr = dotjoinattr(attr, &attrsep);
|
||||
if (attrstr == NULL) {
|
||||
Py_XDECREF(attrsep);
|
||||
Py_DECREF(attrstrings);
|
||||
return NULL;
|
||||
}
|
||||
PyTuple_SET_ITEM(attrstrings, i, attrstr);
|
||||
}
|
||||
Py_XDECREF(attrsep);
|
||||
return attrstrings;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
attrgetter_repr(attrgetterobject *ag)
|
||||
{
|
||||
PyObject *repr = NULL;
|
||||
int status = Py_ReprEnter((PyObject *)ag);
|
||||
if (status != 0) {
|
||||
if (status < 0)
|
||||
return NULL;
|
||||
return PyUnicode_FromFormat("%s(...)", Py_TYPE(ag)->tp_name);
|
||||
}
|
||||
|
||||
if (ag->nattrs == 1) {
|
||||
PyObject *attrsep = NULL;
|
||||
PyObject *attr = dotjoinattr(PyTuple_GET_ITEM(ag->attr, 0), &attrsep);
|
||||
if (attr != NULL)
|
||||
repr = PyUnicode_FromFormat("%s(%R)", Py_TYPE(ag)->tp_name, attr);
|
||||
Py_XDECREF(attrsep);
|
||||
}
|
||||
else {
|
||||
PyObject *attrstrings = attrgetter_args(ag);
|
||||
if (attrstrings != NULL) {
|
||||
repr = PyUnicode_FromFormat("%s%R",
|
||||
Py_TYPE(ag)->tp_name, attrstrings);
|
||||
Py_DECREF(attrstrings);
|
||||
}
|
||||
}
|
||||
Py_ReprLeave((PyObject *)ag);
|
||||
return repr;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
attrgetter_reduce(attrgetterobject *ag)
|
||||
{
|
||||
PyObject *attrstrings = attrgetter_args(ag);
|
||||
if (attrstrings == NULL)
|
||||
return NULL;
|
||||
|
||||
return Py_BuildValue("ON", Py_TYPE(ag), attrstrings);
|
||||
}
|
||||
|
||||
static PyMethodDef attrgetter_methods[] = {
|
||||
{"__reduce__", (PyCFunction)attrgetter_reduce, METH_NOARGS,
|
||||
reduce_doc},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(attrgetter_doc,
|
||||
"attrgetter(attr, ...) --> attrgetter object\n\
|
||||
\n\
|
||||
|
|
@ -757,7 +877,7 @@ static PyTypeObject attrgetter_type = {
|
|||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_reserved */
|
||||
0, /* tp_repr */
|
||||
(reprfunc)attrgetter_repr, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
|
|
@ -775,7 +895,7 @@ static PyTypeObject attrgetter_type = {
|
|||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
attrgetter_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
|
|
@ -813,6 +933,13 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
name = PyTuple_GET_ITEM(args, 0);
|
||||
if (!PyUnicode_Check(name)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"method name must be a string");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create methodcallerobject structure */
|
||||
mc = PyObject_GC_New(methodcallerobject, &methodcaller_type);
|
||||
if (mc == NULL)
|
||||
|
|
@ -825,8 +952,8 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
}
|
||||
mc->args = newargs;
|
||||
|
||||
name = PyTuple_GET_ITEM(args, 0);
|
||||
Py_INCREF(name);
|
||||
PyUnicode_InternInPlace(&name);
|
||||
mc->name = name;
|
||||
|
||||
Py_XINCREF(kwds);
|
||||
|
|
@ -869,6 +996,142 @@ methodcaller_call(methodcallerobject *mc, PyObject *args, PyObject *kw)
|
|||
return result;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
methodcaller_repr(methodcallerobject *mc)
|
||||
{
|
||||
PyObject *argreprs, *repr = NULL, *sep, *joinedargreprs;
|
||||
Py_ssize_t numtotalargs, numposargs, numkwdargs, i;
|
||||
int status = Py_ReprEnter((PyObject *)mc);
|
||||
if (status != 0) {
|
||||
if (status < 0)
|
||||
return NULL;
|
||||
return PyUnicode_FromFormat("%s(...)", Py_TYPE(mc)->tp_name);
|
||||
}
|
||||
|
||||
if (mc->kwds != NULL) {
|
||||
numkwdargs = PyDict_Size(mc->kwds);
|
||||
if (numkwdargs < 0) {
|
||||
Py_ReprLeave((PyObject *)mc);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
numkwdargs = 0;
|
||||
}
|
||||
|
||||
numposargs = PyTuple_GET_SIZE(mc->args);
|
||||
numtotalargs = numposargs + numkwdargs;
|
||||
|
||||
if (numtotalargs == 0) {
|
||||
repr = PyUnicode_FromFormat("%s(%R)", Py_TYPE(mc)->tp_name, mc->name);
|
||||
Py_ReprLeave((PyObject *)mc);
|
||||
return repr;
|
||||
}
|
||||
|
||||
argreprs = PyTuple_New(numtotalargs);
|
||||
if (argreprs == NULL) {
|
||||
Py_ReprLeave((PyObject *)mc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < numposargs; ++i) {
|
||||
PyObject *onerepr = PyObject_Repr(PyTuple_GET_ITEM(mc->args, i));
|
||||
if (onerepr == NULL)
|
||||
goto done;
|
||||
PyTuple_SET_ITEM(argreprs, i, onerepr);
|
||||
}
|
||||
|
||||
if (numkwdargs != 0) {
|
||||
PyObject *key, *value;
|
||||
Py_ssize_t pos = 0;
|
||||
while (PyDict_Next(mc->kwds, &pos, &key, &value)) {
|
||||
PyObject *onerepr = PyUnicode_FromFormat("%U=%R", key, value);
|
||||
if (onerepr == NULL)
|
||||
goto done;
|
||||
if (i >= numtotalargs) {
|
||||
i = -1;
|
||||
break;
|
||||
}
|
||||
PyTuple_SET_ITEM(argreprs, i, onerepr);
|
||||
++i;
|
||||
}
|
||||
if (i != numtotalargs) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"keywords dict changed size during iteration");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
sep = PyUnicode_FromString(", ");
|
||||
if (sep == NULL)
|
||||
goto done;
|
||||
|
||||
joinedargreprs = PyUnicode_Join(sep, argreprs);
|
||||
Py_DECREF(sep);
|
||||
if (joinedargreprs == NULL)
|
||||
goto done;
|
||||
|
||||
repr = PyUnicode_FromFormat("%s(%R, %U)", Py_TYPE(mc)->tp_name,
|
||||
mc->name, joinedargreprs);
|
||||
Py_DECREF(joinedargreprs);
|
||||
|
||||
done:
|
||||
Py_DECREF(argreprs);
|
||||
Py_ReprLeave((PyObject *)mc);
|
||||
return repr;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
methodcaller_reduce(methodcallerobject *mc)
|
||||
{
|
||||
PyObject *newargs;
|
||||
if (!mc->kwds || PyDict_Size(mc->kwds) == 0) {
|
||||
Py_ssize_t i;
|
||||
Py_ssize_t callargcount = PyTuple_GET_SIZE(mc->args);
|
||||
newargs = PyTuple_New(1 + callargcount);
|
||||
if (newargs == NULL)
|
||||
return NULL;
|
||||
Py_INCREF(mc->name);
|
||||
PyTuple_SET_ITEM(newargs, 0, mc->name);
|
||||
for (i = 0; i < callargcount; ++i) {
|
||||
PyObject *arg = PyTuple_GET_ITEM(mc->args, i);
|
||||
Py_INCREF(arg);
|
||||
PyTuple_SET_ITEM(newargs, i + 1, arg);
|
||||
}
|
||||
return Py_BuildValue("ON", Py_TYPE(mc), newargs);
|
||||
}
|
||||
else {
|
||||
PyObject *functools;
|
||||
PyObject *partial;
|
||||
PyObject *constructor;
|
||||
_Py_IDENTIFIER(partial);
|
||||
functools = PyImport_ImportModule("functools");
|
||||
if (!functools)
|
||||
return NULL;
|
||||
partial = _PyObject_GetAttrId(functools, &PyId_partial);
|
||||
Py_DECREF(functools);
|
||||
if (!partial)
|
||||
return NULL;
|
||||
newargs = PyTuple_New(2);
|
||||
if (newargs == NULL) {
|
||||
Py_DECREF(partial);
|
||||
return NULL;
|
||||
}
|
||||
Py_INCREF(Py_TYPE(mc));
|
||||
PyTuple_SET_ITEM(newargs, 0, (PyObject *)Py_TYPE(mc));
|
||||
Py_INCREF(mc->name);
|
||||
PyTuple_SET_ITEM(newargs, 1, mc->name);
|
||||
constructor = PyObject_Call(partial, newargs, mc->kwds);
|
||||
Py_DECREF(newargs);
|
||||
Py_DECREF(partial);
|
||||
return Py_BuildValue("NO", constructor, mc->args);
|
||||
}
|
||||
}
|
||||
|
||||
static PyMethodDef methodcaller_methods[] = {
|
||||
{"__reduce__", (PyCFunction)methodcaller_reduce, METH_NOARGS,
|
||||
reduce_doc},
|
||||
{NULL}
|
||||
};
|
||||
PyDoc_STRVAR(methodcaller_doc,
|
||||
"methodcaller(name, ...) --> methodcaller object\n\
|
||||
\n\
|
||||
|
|
@ -888,7 +1151,7 @@ static PyTypeObject methodcaller_type = {
|
|||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_reserved */
|
||||
0, /* tp_repr */
|
||||
(reprfunc)methodcaller_repr, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
|
|
@ -906,7 +1169,7 @@ static PyTypeObject methodcaller_type = {
|
|||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
methodcaller_methods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue