bpo-40077: Convert _operator to use PyType_FromSpec (GH-21954)

diff --git a/Modules/_operator.c b/Modules/_operator.c
index 8a54829..c9d38aa 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 @@
     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 @@
     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 @@
 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 @@
 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 @@
     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 @@
         }
     }
 
+    _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 @@
 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 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 @@
     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 @@
         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 @@
 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 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 @@
     {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