summaryrefslogtreecommitdiffstats
path: root/Modules/_operator.c
diff options
context:
space:
mode:
authorDennis Sweeney <36520290+sweeneyde@users.noreply.github.com>2022-02-10 21:57:47 (GMT)
committerGitHub <noreply@github.com>2022-02-10 21:57:47 (GMT)
commit0a145069e807fdafd1fa0315b9bc22da363d2d39 (patch)
tree822723800ac79f8544d19a7c90c4a36e045428c8 /Modules/_operator.c
parentd7a5aca982def155a9255893cefcc1493c127c9c (diff)
downloadcpython-0a145069e807fdafd1fa0315b9bc22da363d2d39.zip
cpython-0a145069e807fdafd1fa0315b9bc22da363d2d39.tar.gz
cpython-0a145069e807fdafd1fa0315b9bc22da363d2d39.tar.bz2
bpo-44953: Add vectorcall for itemgetter and attrgetter instances (GH-27828)
Diffstat (limited to 'Modules/_operator.c')
-rw-r--r--Modules/_operator.c90
1 files changed, 78 insertions, 12 deletions
diff --git a/Modules/_operator.c b/Modules/_operator.c
index 10adabc..fdc3176 100644
--- a/Modules/_operator.c
+++ b/Modules/_operator.c
@@ -1,5 +1,6 @@
#include "Python.h"
#include "pycore_moduleobject.h" // _PyModule_GetState()
+#include "structmember.h" // PyMemberDef
#include "pycore_runtime.h" // _Py_ID()
#include "clinic/_operator.c.h"
@@ -974,8 +975,15 @@ typedef struct {
Py_ssize_t nitems;
PyObject *item;
Py_ssize_t index; // -1 unless *item* is a single non-negative integer index
+ vectorcallfunc vectorcall;
} itemgetterobject;
+// Forward declarations
+static PyObject *
+itemgetter_vectorcall(PyObject *, PyObject *const *, size_t, PyObject *);
+static PyObject *
+itemgetter_call_impl(itemgetterobject *, PyObject *);
+
/* AC 3.5: treats first argument as an iterable, otherwise uses *args */
static PyObject *
itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
@@ -1021,6 +1029,7 @@ itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
}
}
+ ig->vectorcall = (vectorcallfunc)itemgetter_vectorcall;
PyObject_GC_Track(ig);
return (PyObject *)ig;
}
@@ -1053,16 +1062,33 @@ itemgetter_traverse(itemgetterobject *ig, visitproc visit, void *arg)
static PyObject *
itemgetter_call(itemgetterobject *ig, PyObject *args, PyObject *kw)
{
- PyObject *obj, *result;
- Py_ssize_t i, nitems=ig->nitems;
-
assert(PyTuple_CheckExact(args));
if (!_PyArg_NoKeywords("itemgetter", kw))
return NULL;
if (!_PyArg_CheckPositional("itemgetter", PyTuple_GET_SIZE(args), 1, 1))
return NULL;
+ return itemgetter_call_impl(ig, PyTuple_GET_ITEM(args, 0));
+}
- obj = PyTuple_GET_ITEM(args, 0);
+static PyObject *
+itemgetter_vectorcall(PyObject *ig, PyObject *const *args,
+ size_t nargsf, PyObject *kwnames)
+{
+ if (!_PyArg_NoKwnames("itemgetter", kwnames)) {
+ return NULL;
+ }
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (!_PyArg_CheckPositional("itemgetter", nargs, 1, 1)) {
+ return NULL;
+ }
+ return itemgetter_call_impl((itemgetterobject *)ig, args[0]);
+}
+
+static PyObject *
+itemgetter_call_impl(itemgetterobject *ig, PyObject *obj)
+{
+ PyObject *result;
+ Py_ssize_t i, nitems=ig->nitems;
if (nitems == 1) {
if (ig->index >= 0
&& PyTuple_CheckExact(obj)
@@ -1130,6 +1156,11 @@ static PyMethodDef itemgetter_methods[] = {
{NULL}
};
+static PyMemberDef itemgetter_members[] = {
+ {"__vectorcalloffset__", T_PYSSIZET, offsetof(itemgetterobject, vectorcall), READONLY},
+ {NULL} /* Sentinel */
+};
+
PyDoc_STRVAR(itemgetter_doc,
"itemgetter(item, ...) --> itemgetter object\n\
\n\
@@ -1144,6 +1175,7 @@ static PyType_Slot itemgetter_type_slots[] = {
{Py_tp_traverse, itemgetter_traverse},
{Py_tp_clear, itemgetter_clear},
{Py_tp_methods, itemgetter_methods},
+ {Py_tp_members, itemgetter_members},
{Py_tp_new, itemgetter_new},
{Py_tp_getattro, PyObject_GenericGetAttr},
{Py_tp_repr, itemgetter_repr},
@@ -1155,7 +1187,7 @@ static PyType_Spec itemgetter_type_spec = {
.basicsize = sizeof(itemgetterobject),
.itemsize = 0,
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
- Py_TPFLAGS_IMMUTABLETYPE),
+ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_VECTORCALL),
.slots = itemgetter_type_slots,
};
@@ -1165,8 +1197,15 @@ typedef struct {
PyObject_HEAD
Py_ssize_t nattrs;
PyObject *attr;
+ vectorcallfunc vectorcall;
} attrgetterobject;
+// Forward declarations
+static PyObject *
+attrgetter_vectorcall(PyObject *, PyObject *const *, size_t, PyObject *);
+static PyObject *
+attrgetter_call_impl(attrgetterobject *, PyObject *);
+
/* AC 3.5: treats first argument as an iterable, otherwise uses *args */
static PyObject *
attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
@@ -1210,7 +1249,7 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
kind = PyUnicode_KIND(item);
data = PyUnicode_DATA(item);
- /* check whethere the string is dotted */
+ /* check whether the string is dotted */
dot_count = 0;
for (char_idx = 0; char_idx < item_len; ++char_idx) {
if (PyUnicode_READ(kind, data, char_idx) == '.')
@@ -1276,6 +1315,7 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
ag->attr = attr;
ag->nattrs = nattrs;
+ ag->vectorcall = (vectorcallfunc)attrgetter_vectorcall;
PyObject_GC_Track(ag);
return (PyObject *)ag;
@@ -1342,16 +1382,36 @@ dotted_getattr(PyObject *obj, PyObject *attr)
static PyObject *
attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw)
{
- PyObject *obj, *result;
- Py_ssize_t i, nattrs=ag->nattrs;
-
if (!_PyArg_NoKeywords("attrgetter", kw))
return NULL;
if (!_PyArg_CheckPositional("attrgetter", PyTuple_GET_SIZE(args), 1, 1))
return NULL;
- obj = PyTuple_GET_ITEM(args, 0);
- if (ag->nattrs == 1) /* ag->attr is always a tuple */
+ return attrgetter_call_impl(ag, PyTuple_GET_ITEM(args, 0));
+}
+
+static PyObject *
+attrgetter_vectorcall(PyObject *ag, PyObject *const *args, size_t nargsf, PyObject *kwnames)
+{
+ if (!_PyArg_NoKwnames("attrgetter", kwnames)) {
+ return NULL;
+ }
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (!_PyArg_CheckPositional("attrgetter", nargs, 1, 1)) {
+ return NULL;
+ }
+ return attrgetter_call_impl((attrgetterobject *)ag, args[0]);
+}
+
+static PyObject *
+attrgetter_call_impl(attrgetterobject *ag, PyObject *obj)
+{
+ PyObject *result;
+ Py_ssize_t i, nattrs=ag->nattrs;
+
+ if (ag->nattrs == 1) {
+ /* ag->attr is always a tuple */
return dotted_getattr(obj, PyTuple_GET_ITEM(ag->attr, 0));
+ }
assert(PyTuple_Check(ag->attr));
assert(PyTuple_GET_SIZE(ag->attr) == nattrs);
@@ -1460,6 +1520,11 @@ static PyMethodDef attrgetter_methods[] = {
{NULL}
};
+static PyMemberDef attrgetter_members[] = {
+ {"__vectorcalloffset__", T_PYSSIZET, offsetof(attrgetterobject, vectorcall), READONLY},
+ {NULL} /* Sentinel*/
+};
+
PyDoc_STRVAR(attrgetter_doc,
"attrgetter(attr, ...) --> attrgetter object\n\
\n\
@@ -1476,6 +1541,7 @@ static PyType_Slot attrgetter_type_slots[] = {
{Py_tp_traverse, attrgetter_traverse},
{Py_tp_clear, attrgetter_clear},
{Py_tp_methods, attrgetter_methods},
+ {Py_tp_members, attrgetter_members},
{Py_tp_new, attrgetter_new},
{Py_tp_getattro, PyObject_GenericGetAttr},
{Py_tp_repr, attrgetter_repr},
@@ -1487,7 +1553,7 @@ static PyType_Spec attrgetter_type_spec = {
.basicsize = sizeof(attrgetterobject),
.itemsize = 0,
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
- Py_TPFLAGS_IMMUTABLETYPE),
+ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_VECTORCALL),
.slots = attrgetter_type_slots,
};