From 31967fd8d0220af84e2698172d1378bffc8cd851 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Wed, 26 Aug 2020 17:22:27 +0000 Subject: [PATCH] bpo-40077: Convert _operator to use PyType_FromSpec (GH-21954) --- .../2020-08-25-22-43-33.bpo-40077.vcxSUa.rst | 1 + Modules/_operator.c | 298 +++++++++--------- 2 files changed, 143 insertions(+), 156 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-08-25-22-43-33.bpo-40077.vcxSUa.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-25-22-43-33.bpo-40077.vcxSUa.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-25-22-43-33.bpo-40077.vcxSUa.rst new file mode 100644 index 00000000000..ee950010e6d --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-08-25-22-43-33.bpo-40077.vcxSUa.rst @@ -0,0 +1 @@ +Convert :mod:`_operator` to use :c:func:`PyType_FromSpec`. diff --git a/Modules/_operator.c b/Modules/_operator.c index 8a54829e5bb..c9d38aa3423 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -3,6 +3,20 @@ #include "clinic/_operator.c.h" +typedef struct { + PyObject *itemgetter_type; + PyObject *attrgetter_type; + PyObject *methodcaller_type; +} _operator_state; + +static inline _operator_state* +get_operator_state(PyObject *module) +{ + void *state = PyModule_GetState(module); + assert(state != NULL); + return (_operator_state *)state; +} + /*[clinic input] module _operator [clinic start generated code]*/ @@ -942,8 +956,6 @@ typedef struct { Py_ssize_t index; // -1 unless *item* is a single non-negative integer index } itemgetterobject; -static PyTypeObject itemgetter_type; - /* AC 3.5: treats first argument as an iterable, otherwise uses *args */ static PyObject * itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) @@ -960,13 +972,15 @@ itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (nitems <= 1) { if (!PyArg_UnpackTuple(args, "itemgetter", 1, 1, &item)) return NULL; - } else + } else { item = args; - + } + _operator_state *state = PyType_GetModuleState(type); /* create itemgetterobject structure */ - ig = PyObject_GC_New(itemgetterobject, &itemgetter_type); - if (ig == NULL) + ig = PyObject_GC_New(itemgetterobject, (PyTypeObject *) state->itemgetter_type); + if (ig == NULL) { return NULL; + } Py_INCREF(item); ig->item = item; @@ -994,9 +1008,11 @@ itemgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static void itemgetter_dealloc(itemgetterobject *ig) { + PyTypeObject *tp = Py_TYPE(ig); PyObject_GC_UnTrack(ig); Py_XDECREF(ig->item); - PyObject_GC_Del(ig); + tp->tp_free(ig); + Py_DECREF(tp); } static int @@ -1093,49 +1109,25 @@ Return a callable object that fetches the given item(s) from its operand.\n\ After f = itemgetter(2), the call f(r) returns r[2].\n\ After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3])"); -static PyTypeObject itemgetter_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "operator.itemgetter", /* tp_name */ - sizeof(itemgetterobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)itemgetter_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)itemgetter_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - (ternaryfunc)itemgetter_call, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - itemgetter_doc, /* tp_doc */ - (traverseproc)itemgetter_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - itemgetter_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - itemgetter_new, /* tp_new */ - 0, /* tp_free */ +static PyType_Slot itemgetter_type_slots[] = { + {Py_tp_doc, (void *)itemgetter_doc}, + {Py_tp_dealloc, itemgetter_dealloc}, + {Py_tp_call, itemgetter_call}, + {Py_tp_traverse, itemgetter_traverse}, + {Py_tp_methods, itemgetter_methods}, + {Py_tp_new, itemgetter_new}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_repr, itemgetter_repr}, + {0, 0} }; +static PyType_Spec itemgetter_type_spec = { + .name = "operator.itemgetter", + .basicsize = sizeof(itemgetterobject), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + .slots = itemgetter_type_slots, +}; /* attrgetter object **********************************************************/ @@ -1145,8 +1137,6 @@ typedef struct { PyObject *attr; } attrgetterobject; -static PyTypeObject attrgetter_type; - /* AC 3.5: treats first argument as an iterable, otherwise uses *args */ static PyObject * attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) @@ -1246,8 +1236,9 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } } + _operator_state *state = PyType_GetModuleState(type); /* create attrgetterobject structure */ - ag = PyObject_GC_New(attrgetterobject, &attrgetter_type); + ag = PyObject_GC_New(attrgetterobject, (PyTypeObject *)state->attrgetter_type); if (ag == NULL) { Py_DECREF(attr); return NULL; @@ -1263,9 +1254,11 @@ attrgetter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static void attrgetter_dealloc(attrgetterobject *ag) { + PyTypeObject *tp = Py_TYPE(ag); PyObject_GC_UnTrack(ag); Py_XDECREF(ag->attr); - PyObject_GC_Del(ag); + tp->tp_free(ag); + Py_DECREF(tp); } static int @@ -1438,47 +1431,24 @@ After g = attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).\n\ After h = attrgetter('name.first', 'name.last'), the call h(r) returns\n\ (r.name.first, r.name.last)."); -static PyTypeObject attrgetter_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "operator.attrgetter", /* tp_name */ - sizeof(attrgetterobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)attrgetter_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)attrgetter_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - (ternaryfunc)attrgetter_call, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - attrgetter_doc, /* tp_doc */ - (traverseproc)attrgetter_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - attrgetter_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - attrgetter_new, /* tp_new */ - 0, /* tp_free */ +static PyType_Slot attrgetter_type_slots[] = { + {Py_tp_doc, (void *)attrgetter_doc}, + {Py_tp_dealloc, attrgetter_dealloc}, + {Py_tp_call, attrgetter_call}, + {Py_tp_traverse, attrgetter_traverse}, + {Py_tp_methods, attrgetter_methods}, + {Py_tp_new, attrgetter_new}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_repr, attrgetter_repr}, + {0, 0} +}; + +static PyType_Spec attrgetter_type_spec = { + .name = "operator.attrgetter", + .basicsize = sizeof(attrgetterobject), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + .slots = attrgetter_type_slots, }; @@ -1491,8 +1461,6 @@ typedef struct { PyObject *kwds; } methodcallerobject; -static PyTypeObject methodcaller_type; - /* AC 3.5: variable number of arguments, not currently support by AC */ static PyObject * methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds) @@ -1513,10 +1481,12 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } + _operator_state *state = PyType_GetModuleState(type); /* create methodcallerobject structure */ - mc = PyObject_GC_New(methodcallerobject, &methodcaller_type); - if (mc == NULL) + mc = PyObject_GC_New(methodcallerobject, (PyTypeObject *)state->methodcaller_type); + if (mc == NULL) { return NULL; + } name = PyTuple_GET_ITEM(args, 0); Py_INCREF(name); @@ -1539,11 +1509,13 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static void methodcaller_dealloc(methodcallerobject *mc) { + PyTypeObject *tp = Py_TYPE(mc); PyObject_GC_UnTrack(mc); Py_XDECREF(mc->name); Py_XDECREF(mc->args); Py_XDECREF(mc->kwds); - PyObject_GC_Del(mc); + tp->tp_free(mc); + Py_DECREF(tp); } static int @@ -1704,63 +1676,52 @@ After f = methodcaller('name'), the call f(r) returns r.name().\n\ After g = methodcaller('name', 'date', foo=1), the call g(r) returns\n\ r.name('date', foo=1)."); -static PyTypeObject methodcaller_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "operator.methodcaller", /* tp_name */ - sizeof(methodcallerobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)methodcaller_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)methodcaller_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - (ternaryfunc)methodcaller_call, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ - methodcaller_doc, /* tp_doc */ - (traverseproc)methodcaller_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - methodcaller_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - methodcaller_new, /* tp_new */ - 0, /* tp_free */ +static PyType_Slot methodcaller_type_slots[] = { + {Py_tp_doc, (void *)methodcaller_doc}, + {Py_tp_dealloc, methodcaller_dealloc}, + {Py_tp_call, methodcaller_call}, + {Py_tp_traverse, methodcaller_traverse}, + {Py_tp_methods, methodcaller_methods}, + {Py_tp_new, methodcaller_new}, + {Py_tp_getattro, PyObject_GenericGetAttr}, + {Py_tp_repr, methodcaller_repr}, + {0, 0} }; +static PyType_Spec methodcaller_type_spec = { + .name = "operator.methodcaller", + .basicsize = sizeof(methodcallerobject), + .itemsize = 0, + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + .slots = methodcaller_type_slots, +}; static int operator_exec(PyObject *module) { - PyTypeObject *types[] = { - &itemgetter_type, - &attrgetter_type, - &methodcaller_type - }; + _operator_state *state = get_operator_state(module); + state->attrgetter_type = PyType_FromModuleAndSpec(module, &attrgetter_type_spec, NULL); + if (state->attrgetter_type == NULL) { + return -1; + } + if (PyModule_AddType(module, (PyTypeObject *)state->attrgetter_type) < 0) { + return -1; + } - for (size_t i = 0; i < Py_ARRAY_LENGTH(types); i++) { - if (PyModule_AddType(module, types[i]) < 0) { - return -1; - } + state->itemgetter_type = PyType_FromModuleAndSpec(module, &itemgetter_type_spec, NULL); + if (state->itemgetter_type == NULL) { + return -1; + } + if (PyModule_AddType(module, (PyTypeObject *)state->itemgetter_type) < 0) { + return -1; + } + + state->methodcaller_type = PyType_FromModuleAndSpec(module, &methodcaller_type_spec, NULL); + if (state->methodcaller_type == NULL) { + return -1; + } + if (PyModule_AddType(module, (PyTypeObject *)state->methodcaller_type) < 0) { + return -1; } return 0; @@ -1772,17 +1733,42 @@ static struct PyModuleDef_Slot operator_slots[] = { {0, NULL} }; +static int +operator_traverse(PyObject *module, visitproc visit, void *arg) +{ + _operator_state *state = get_operator_state(module); + Py_VISIT(state->attrgetter_type); + Py_VISIT(state->itemgetter_type); + Py_VISIT(state->methodcaller_type); + return 0; +} + +static int +operator_clear(PyObject *module) +{ + _operator_state *state = get_operator_state(module); + Py_CLEAR(state->attrgetter_type); + Py_CLEAR(state->itemgetter_type); + Py_CLEAR(state->methodcaller_type); + return 0; +} + +static void +operator_free(void *module) +{ + operator_clear((PyObject *)module); +} static struct PyModuleDef operatormodule = { PyModuleDef_HEAD_INIT, - "_operator", - operator_doc, - 0, - operator_methods, - operator_slots, - NULL, - NULL, - NULL + .m_name = "_operator", + .m_doc = operator_doc, + .m_size = sizeof(_operator_state), + .m_methods = operator_methods, + .m_slots = operator_slots, + .m_traverse = operator_traverse, + .m_clear = operator_clear, + .m_free = operator_free, }; PyMODINIT_FUNC