@@ -1147,7 +1147,6 @@ Return a callable object that fetches the given item(s) from its operand.\n\
1147
1147
After f = itemgetter(2), the call f(r) returns r[2].\n\
1148
1148
After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3])" );
1149
1149
1150
-
1151
1150
static PyType_Slot itemgetter_type_slots [] = {
1152
1151
{Py_tp_doc , (void * )itemgetter_doc },
1153
1152
{Py_tp_dealloc , itemgetter_dealloc },
@@ -1177,8 +1176,15 @@ typedef struct {
1177
1176
PyObject_HEAD
1178
1177
Py_ssize_t nattrs ;
1179
1178
PyObject * attr ;
1179
+ vectorcallfunc vectorcall ;
1180
1180
} attrgetterobject ;
1181
1181
1182
+ // Forward declarations
1183
+ static PyObject *
1184
+ attrgetter_vectorcall (PyObject * , PyObject * const * , size_t , PyObject * );
1185
+ static PyObject *
1186
+ attrgetter_call_impl (attrgetterobject * , PyObject * );
1187
+
1182
1188
/* AC 3.5: treats first argument as an iterable, otherwise uses *args */
1183
1189
static PyObject *
1184
1190
attrgetter_new (PyTypeObject * type , PyObject * args , PyObject * kwds )
@@ -1222,7 +1228,7 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1222
1228
kind = PyUnicode_KIND (item );
1223
1229
data = PyUnicode_DATA (item );
1224
1230
1225
- /* check whethere the string is dotted */
1231
+ /* check whether the string is dotted */
1226
1232
dot_count = 0 ;
1227
1233
for (char_idx = 0 ; char_idx < item_len ; ++ char_idx ) {
1228
1234
if (PyUnicode_READ (kind , data , char_idx ) == '.' )
@@ -1288,6 +1294,7 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1288
1294
1289
1295
ag -> attr = attr ;
1290
1296
ag -> nattrs = nattrs ;
1297
+ ag -> vectorcall = (vectorcallfunc )attrgetter_vectorcall ;
1291
1298
1292
1299
PyObject_GC_Track (ag );
1293
1300
return (PyObject * )ag ;
@@ -1354,16 +1361,36 @@ dotted_getattr(PyObject *obj, PyObject *attr)
1354
1361
static PyObject *
1355
1362
attrgetter_call (attrgetterobject * ag , PyObject * args , PyObject * kw )
1356
1363
{
1357
- PyObject * obj , * result ;
1358
- Py_ssize_t i , nattrs = ag -> nattrs ;
1359
-
1360
1364
if (!_PyArg_NoKeywords ("attrgetter" , kw ))
1361
1365
return NULL ;
1362
1366
if (!_PyArg_CheckPositional ("attrgetter" , PyTuple_GET_SIZE (args ), 1 , 1 ))
1363
1367
return NULL ;
1364
- obj = PyTuple_GET_ITEM (args , 0 );
1365
- if (ag -> nattrs == 1 ) /* ag->attr is always a tuple */
1368
+ return attrgetter_call_impl (ag , PyTuple_GET_ITEM (args , 0 ));
1369
+ }
1370
+
1371
+ static PyObject *
1372
+ attrgetter_vectorcall (PyObject * ag , PyObject * const * args , size_t nargsf , PyObject * kwnames )
1373
+ {
1374
+ if (!_PyArg_NoKwnames ("attrgetter" , kwnames )) {
1375
+ return NULL ;
1376
+ }
1377
+ Py_ssize_t nargs = PyVectorcall_NARGS (nargsf );
1378
+ if (!_PyArg_CheckPositional ("attrgetter" , nargs , 1 , 1 )) {
1379
+ return NULL ;
1380
+ }
1381
+ return attrgetter_call_impl ((attrgetterobject * )ag , args [0 ]);
1382
+ }
1383
+
1384
+ static PyObject *
1385
+ attrgetter_call_impl (attrgetterobject * ag , PyObject * obj )
1386
+ {
1387
+ PyObject * result ;
1388
+ Py_ssize_t i , nattrs = ag -> nattrs ;
1389
+
1390
+ if (ag -> nattrs == 1 ) {
1391
+ /* ag->attr is always a tuple */
1366
1392
return dotted_getattr (obj , PyTuple_GET_ITEM (ag -> attr , 0 ));
1393
+ }
1367
1394
1368
1395
assert (PyTuple_Check (ag -> attr ));
1369
1396
assert (PyTuple_GET_SIZE (ag -> attr ) == nattrs );
@@ -1472,6 +1499,11 @@ static PyMethodDef attrgetter_methods[] = {
1472
1499
{NULL }
1473
1500
};
1474
1501
1502
+ static PyMemberDef attrgetter_members [] = {
1503
+ {"__vectorcalloffset__" , T_PYSSIZET , offsetof(attrgetterobject , vectorcall ), READONLY },
1504
+ {NULL } /* Sentinel*/
1505
+ };
1506
+
1475
1507
PyDoc_STRVAR (attrgetter_doc ,
1476
1508
"attrgetter(attr, ...) --> attrgetter object\n\
1477
1509
\n\
@@ -1488,6 +1520,7 @@ static PyType_Slot attrgetter_type_slots[] = {
1488
1520
{Py_tp_traverse , attrgetter_traverse },
1489
1521
{Py_tp_clear , attrgetter_clear },
1490
1522
{Py_tp_methods , attrgetter_methods },
1523
+ {Py_tp_members , attrgetter_members },
1491
1524
{Py_tp_new , attrgetter_new },
1492
1525
{Py_tp_getattro , PyObject_GenericGetAttr },
1493
1526
{Py_tp_repr , attrgetter_repr },
@@ -1499,7 +1532,7 @@ static PyType_Spec attrgetter_type_spec = {
1499
1532
.basicsize = sizeof (attrgetterobject ),
1500
1533
.itemsize = 0 ,
1501
1534
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
1502
- Py_TPFLAGS_IMMUTABLETYPE ),
1535
+ Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_VECTORCALL ),
1503
1536
.slots = attrgetter_type_slots ,
1504
1537
};
1505
1538
0 commit comments