]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-103092: Isolate `_collections` (#103093)
authorErlend E. Aasland <erlend.aasland@protonmail.com>
Wed, 12 Apr 2023 12:51:28 +0000 (14:51 +0200)
committerGitHub <noreply@github.com>
Wed, 12 Apr 2023 12:51:28 +0000 (18:21 +0530)
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Lib/collections/__init__.py
Misc/NEWS.d/next/Library/2023-04-09-06-59-36.gh-issue-103092.vskbro.rst [new file with mode: 0644]
Modules/_collectionsmodule.c
Modules/clinic/_collectionsmodule.c.h
Tools/c-analyzer/cpython/globals-to-fix.tsv

index a5393aad4249c0a3f863f94fe0c533410f04d631..03ca2d7e18f6f02cae8947c3125c7888407a9d65 100644 (file)
@@ -45,6 +45,11 @@ except ImportError:
 else:
     _collections_abc.MutableSequence.register(deque)
 
+try:
+    from _collections import _deque_iterator
+except ImportError:
+    pass
+
 try:
     from _collections import defaultdict
 except ImportError:
diff --git a/Misc/NEWS.d/next/Library/2023-04-09-06-59-36.gh-issue-103092.vskbro.rst b/Misc/NEWS.d/next/Library/2023-04-09-06-59-36.gh-issue-103092.vskbro.rst
new file mode 100644 (file)
index 0000000..6977c14
--- /dev/null
@@ -0,0 +1 @@
+Isolate :mod:`!_collections` (apply :pep:`687`). Patch by Erlend E. Aasland.\r
index 9d8aef1e677157ad5af55d4795412821126a342c..a9b1425177c3d76c282d1053cde53e7083e55edb 100644 (file)
@@ -1,17 +1,56 @@
 #include "Python.h"
 #include "pycore_call.h"          // _PyObject_CallNoArgs()
 #include "pycore_long.h"          // _PyLong_GetZero()
+#include "pycore_moduleobject.h"  // _PyModule_GetState()
+#include "pycore_typeobject.h"    // _PyType_GetModuleState()
 #include "structmember.h"         // PyMemberDef
 #include <stddef.h>
 
+typedef struct {
+    PyTypeObject *deque_type;
+    PyTypeObject *defdict_type;
+    PyTypeObject *dequeiter_type;
+    PyTypeObject *dequereviter_type;
+    PyTypeObject *tuplegetter_type;
+} collections_state;
+
+static inline collections_state *
+get_module_state(PyObject *mod)
+{
+    void *state = _PyModule_GetState(mod);
+    assert(state != NULL);
+    return (collections_state *)state;
+}
+
+static inline collections_state *
+get_module_state_by_cls(PyTypeObject *cls)
+{
+    void *state = _PyType_GetModuleState(cls);
+    assert(state != NULL);
+    return (collections_state *)state;
+}
+
+static struct PyModuleDef _collectionsmodule;
+
+static inline collections_state *
+find_module_state_by_def(PyTypeObject *type)
+{
+    PyObject *mod = PyType_GetModuleByDef(type, &_collectionsmodule);
+    assert(mod != NULL);
+    return get_module_state(mod);
+}
+
 /*[clinic input]
 module _collections
-class _tuplegetter "_tuplegetterobject *" "&tuplegetter_type"
+class _tuplegetter "_tuplegetterobject *" "clinic_state()->tuplegetter_type"
 [clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a8ece4ccad7e30ac]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7356042a89862e0e]*/
 
-static PyTypeObject tuplegetter_type;
+/* We can safely assume type to be the defining class,
+ * since tuplegetter is not a base type */
+#define clinic_state() (get_module_state_by_cls(type))
 #include "clinic/_collectionsmodule.c.h"
+#undef clinic_state
 
 /* collections module implementation of a deque() datatype
    Written and maintained by Raymond D. Hettinger <python@rcn.com>
@@ -94,8 +133,6 @@ typedef struct {
     PyObject *weakreflist;
 } dequeobject;
 
-static PyTypeObject deque_type;
-
 /* For debug builds, add error checking to track the endpoints
  * in the chain of links.  The goal is to make sure that link
  * assignments only take place at endpoints so that links already
@@ -484,11 +521,13 @@ deque_copy(PyObject *deque, PyObject *Py_UNUSED(ignored))
 {
     PyObject *result;
     dequeobject *old_deque = (dequeobject *)deque;
-    if (Py_IS_TYPE(deque, &deque_type)) {
+    collections_state *state = find_module_state_by_def(Py_TYPE(deque));
+    if (Py_IS_TYPE(deque, state->deque_type)) {
         dequeobject *new_deque;
         PyObject *rv;
 
-        new_deque = (dequeobject *)deque_new(&deque_type, (PyObject *)NULL, (PyObject *)NULL);
+        new_deque = (dequeobject *)deque_new(state->deque_type,
+                                             (PyObject *)NULL, (PyObject *)NULL);
         if (new_deque == NULL)
             return NULL;
         new_deque->maxlen = old_deque->maxlen;
@@ -511,7 +550,7 @@ deque_copy(PyObject *deque, PyObject *Py_UNUSED(ignored))
     else
         result = PyObject_CallFunction((PyObject *)(Py_TYPE(deque)), "Oi",
                                        deque, old_deque->maxlen, NULL);
-    if (result != NULL && !PyObject_TypeCheck(result, &deque_type)) {
+    if (result != NULL && !PyObject_TypeCheck(result, state->deque_type)) {
         PyErr_Format(PyExc_TypeError,
                      "%.200s() must return a deque, not %.200s",
                      Py_TYPE(deque)->tp_name, Py_TYPE(result)->tp_name);
@@ -529,7 +568,8 @@ deque_concat(dequeobject *deque, PyObject *other)
     PyObject *new_deque, *result;
     int rv;
 
-    rv = PyObject_IsInstance(other, (PyObject *)&deque_type);
+    collections_state *state = find_module_state_by_def(Py_TYPE(deque));
+    rv = PyObject_IsInstance(other, (PyObject *)state->deque_type);
     if (rv <= 0) {
         if (rv == 0) {
             PyErr_Format(PyExc_TypeError,
@@ -1288,6 +1328,7 @@ deque_ass_item(dequeobject *deque, Py_ssize_t i, PyObject *v)
 static void
 deque_dealloc(dequeobject *deque)
 {
+    PyTypeObject *tp = Py_TYPE(deque);
     Py_ssize_t i;
 
     PyObject_GC_UnTrack(deque);
@@ -1303,12 +1344,15 @@ deque_dealloc(dequeobject *deque)
     for (i=0 ; i < deque->numfreeblocks ; i++) {
         PyMem_Free(deque->freeblocks[i]);
     }
-    Py_TYPE(deque)->tp_free(deque);
+    tp->tp_free(deque);
+    Py_DECREF(tp);
 }
 
 static int
 deque_traverse(dequeobject *deque, visitproc visit, void *arg)
 {
+    Py_VISIT(Py_TYPE(deque));
+
     block *b;
     PyObject *item;
     Py_ssize_t index;
@@ -1393,8 +1437,9 @@ deque_richcompare(PyObject *v, PyObject *w, int op)
     Py_ssize_t vs, ws;
     int b, cmp=-1;
 
-    if (!PyObject_TypeCheck(v, &deque_type) ||
-        !PyObject_TypeCheck(w, &deque_type)) {
+    collections_state *state = find_module_state_by_def(Py_TYPE(v));
+    if (!PyObject_TypeCheck(v, state->deque_type) ||
+        !PyObject_TypeCheck(w, state->deque_type)) {
         Py_RETURN_NOTIMPLEMENTED;
     }
 
@@ -1537,19 +1582,6 @@ static PyGetSetDef deque_getset[] = {
     {0}
 };
 
-static PySequenceMethods deque_as_sequence = {
-    (lenfunc)deque_len,                 /* sq_length */
-    (binaryfunc)deque_concat,           /* sq_concat */
-    (ssizeargfunc)deque_repeat,         /* sq_repeat */
-    (ssizeargfunc)deque_item,           /* sq_item */
-    0,                                  /* sq_slice */
-    (ssizeobjargproc)deque_ass_item,    /* sq_ass_item */
-    0,                                  /* sq_ass_slice */
-    (objobjproc)deque_contains,         /* sq_contains */
-    (binaryfunc)deque_inplace_concat,   /* sq_inplace_concat */
-    (ssizeargfunc)deque_inplace_repeat, /* sq_inplace_repeat */
-};
-
 static PyObject *deque_iter(dequeobject *deque);
 static PyObject *deque_reviter(dequeobject *deque, PyObject *Py_UNUSED(ignored));
 PyDoc_STRVAR(reversed_doc,
@@ -1597,54 +1629,53 @@ static PyMethodDef deque_methods[] = {
     {NULL,              NULL}   /* sentinel */
 };
 
+static PyMemberDef deque_members[] = {
+    {"__weaklistoffset__", T_PYSSIZET, offsetof(dequeobject, weakreflist), READONLY},
+    {NULL},
+};
+
 PyDoc_STRVAR(deque_doc,
 "deque([iterable[, maxlen]]) --> deque object\n\
 \n\
 A list-like sequence optimized for data accesses near its endpoints.");
 
-static PyTypeObject deque_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "collections.deque",                /* tp_name */
-    sizeof(dequeobject),                /* tp_basicsize */
-    0,                                  /* tp_itemsize */
-    /* methods */
-    (destructor)deque_dealloc,          /* tp_dealloc */
-    0,                                  /* tp_vectorcall_offset */
-    0,                                  /* tp_getattr */
-    0,                                  /* tp_setattr */
-    0,                                  /* tp_as_async */
-    deque_repr,                         /* tp_repr */
-    0,                                  /* tp_as_number */
-    &deque_as_sequence,                 /* tp_as_sequence */
-    0,                                  /* tp_as_mapping */
-    PyObject_HashNotImplemented,        /* tp_hash */
-    0,                                  /* tp_call */
-    0,                                  /* tp_str */
-    PyObject_GenericGetAttr,            /* tp_getattro */
-    0,                                  /* tp_setattro */
-    0,                                  /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
-    Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_SEQUENCE,
-                                        /* tp_flags */
-    deque_doc,                          /* tp_doc */
-    (traverseproc)deque_traverse,       /* tp_traverse */
-    (inquiry)deque_clear,               /* tp_clear */
-    (richcmpfunc)deque_richcompare,     /* tp_richcompare */
-    offsetof(dequeobject, weakreflist), /* tp_weaklistoffset*/
-    (getiterfunc)deque_iter,            /* tp_iter */
-    0,                                  /* tp_iternext */
-    deque_methods,                      /* tp_methods */
-    0,                                  /* tp_members */
-    deque_getset,                       /* tp_getset */
-    0,                                  /* tp_base */
-    0,                                  /* tp_dict */
-    0,                                  /* tp_descr_get */
-    0,                                  /* tp_descr_set */
-    0,                                  /* tp_dictoffset */
-    (initproc)deque_init,               /* tp_init */
-    PyType_GenericAlloc,                /* tp_alloc */
-    deque_new,                          /* tp_new */
-    PyObject_GC_Del,                    /* tp_free */
+static PyType_Slot deque_slots[] = {
+    {Py_tp_dealloc, deque_dealloc},
+    {Py_tp_repr, deque_repr},
+    {Py_tp_hash, PyObject_HashNotImplemented},
+    {Py_tp_getattro, PyObject_GenericGetAttr},
+    {Py_tp_doc, (void *)deque_doc},
+    {Py_tp_traverse, deque_traverse},
+    {Py_tp_clear, deque_clear},
+    {Py_tp_richcompare, deque_richcompare},
+    {Py_tp_iter, deque_iter},
+    {Py_tp_getset, deque_getset},
+    {Py_tp_init, deque_init},
+    {Py_tp_alloc, PyType_GenericAlloc},
+    {Py_tp_new, deque_new},
+    {Py_tp_free, PyObject_GC_Del},
+    {Py_tp_methods, deque_methods},
+    {Py_tp_members, deque_members},
+
+    // Sequence protocol
+    {Py_sq_length, deque_len},
+    {Py_sq_concat, deque_concat},
+    {Py_sq_repeat, deque_repeat},
+    {Py_sq_item, deque_item},
+    {Py_sq_ass_item, deque_ass_item},
+    {Py_sq_contains, deque_contains},
+    {Py_sq_inplace_concat, deque_inplace_concat},
+    {Py_sq_inplace_repeat, deque_inplace_repeat},
+    {0, NULL},
+};
+
+static PyType_Spec deque_spec = {
+    .name = "collections.deque",
+    .basicsize = sizeof(dequeobject),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+              Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_SEQUENCE |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = deque_slots,
 };
 
 /*********************** Deque Iterator **************************/
@@ -1658,14 +1689,13 @@ typedef struct {
     Py_ssize_t counter;    /* number of items remaining for iteration */
 } dequeiterobject;
 
-static PyTypeObject dequeiter_type;
-
 static PyObject *
 deque_iter(dequeobject *deque)
 {
     dequeiterobject *it;
 
-    it = PyObject_GC_New(dequeiterobject, &dequeiter_type);
+    collections_state *state = find_module_state_by_def(Py_TYPE(deque));
+    it = PyObject_GC_New(dequeiterobject, state->dequeiter_type);
     if (it == NULL)
         return NULL;
     it->b = deque->leftblock;
@@ -1680,17 +1710,27 @@ deque_iter(dequeobject *deque)
 static int
 dequeiter_traverse(dequeiterobject *dio, visitproc visit, void *arg)
 {
+    Py_VISIT(Py_TYPE(dio));
     Py_VISIT(dio->deque);
     return 0;
 }
 
+static int
+dequeiter_clear(dequeiterobject *dio)
+{
+    Py_CLEAR(dio->deque);
+    return 0;
+}
+
 static void
 dequeiter_dealloc(dequeiterobject *dio)
 {
     /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyTypeObject *tp = Py_TYPE(dio);
     PyObject_GC_UnTrack(dio);
-    Py_XDECREF(dio->deque);
+    (void)dequeiter_clear(dio);
     PyObject_GC_Del(dio);
+    Py_DECREF(tp);
 }
 
 static PyObject *
@@ -1726,9 +1766,10 @@ dequeiter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     Py_ssize_t i, index=0;
     PyObject *deque;
     dequeiterobject *it;
-    if (!PyArg_ParseTuple(args, "O!|n", &deque_type, &deque, &index))
+    collections_state *state = get_module_state_by_cls(type);
+    if (!PyArg_ParseTuple(args, "O!|n", state->deque_type, &deque, &index))
         return NULL;
-    assert(type == &dequeiter_type);
+    assert(type == state->dequeiter_type);
 
     it = (dequeiterobject*)deque_iter((dequeobject *)deque);
     if (!it)
@@ -1769,59 +1810,35 @@ static PyMethodDef dequeiter_methods[] = {
     {NULL,              NULL}           /* sentinel */
 };
 
-static PyTypeObject dequeiter_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_collections._deque_iterator",             /* tp_name */
-    sizeof(dequeiterobject),                    /* tp_basicsize */
-    0,                                          /* tp_itemsize */
-    /* methods */
-    (destructor)dequeiter_dealloc,              /* tp_dealloc */
-    0,                                          /* tp_vectorcall_offset */
-    0,                                          /* tp_getattr */
-    0,                                          /* tp_setattr */
-    0,                                          /* tp_as_async */
-    0,                                          /* tp_repr */
-    0,                                          /* tp_as_number */
-    0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* 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 */
-    0,                                          /* tp_doc */
-    (traverseproc)dequeiter_traverse,           /* tp_traverse */
-    0,                                          /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    PyObject_SelfIter,                          /* tp_iter */
-    (iternextfunc)dequeiter_next,               /* tp_iternext */
-    dequeiter_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 */
-    dequeiter_new,                              /* tp_new */
-    0,
+static PyType_Slot dequeiter_slots[] = {
+    {Py_tp_dealloc, dequeiter_dealloc},
+    {Py_tp_getattro, PyObject_GenericGetAttr},
+    {Py_tp_traverse, dequeiter_traverse},
+    {Py_tp_clear, dequeiter_clear},
+    {Py_tp_iter, PyObject_SelfIter},
+    {Py_tp_iternext, dequeiter_next},
+    {Py_tp_methods, dequeiter_methods},
+    {Py_tp_new, dequeiter_new},
+    {0, NULL},
 };
 
-/*********************** Deque Reverse Iterator **************************/
+static PyType_Spec dequeiter_spec = {
+    .name = "collections._deque_iterator",
+    .basicsize = sizeof(dequeiterobject),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = dequeiter_slots,
+};
 
-static PyTypeObject dequereviter_type;
+/*********************** Deque Reverse Iterator **************************/
 
 static PyObject *
 deque_reviter(dequeobject *deque, PyObject *Py_UNUSED(ignored))
 {
     dequeiterobject *it;
+    collections_state *state = find_module_state_by_def(Py_TYPE(deque));
 
-    it = PyObject_GC_New(dequeiterobject, &dequereviter_type);
+    it = PyObject_GC_New(dequeiterobject, state->dequereviter_type);
     if (it == NULL)
         return NULL;
     it->b = deque->rightblock;
@@ -1866,9 +1883,10 @@ dequereviter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     Py_ssize_t i, index=0;
     PyObject *deque;
     dequeiterobject *it;
-    if (!PyArg_ParseTuple(args, "O!|n", &deque_type, &deque, &index))
+    collections_state *state = get_module_state_by_cls(type);
+    if (!PyArg_ParseTuple(args, "O!|n", state->deque_type, &deque, &index))
         return NULL;
-    assert(type == &dequereviter_type);
+    assert(type == state->dequereviter_type);
 
     it = (dequeiterobject*)deque_reviter((dequeobject *)deque, NULL);
     if (!it)
@@ -1889,47 +1907,24 @@ dequereviter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     return (PyObject*)it;
 }
 
-static PyTypeObject dequereviter_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_collections._deque_reverse_iterator",     /* tp_name */
-    sizeof(dequeiterobject),                    /* tp_basicsize */
-    0,                                          /* tp_itemsize */
-    /* methods */
-    (destructor)dequeiter_dealloc,              /* tp_dealloc */
-    0,                                          /* tp_vectorcall_offset */
-    0,                                          /* tp_getattr */
-    0,                                          /* tp_setattr */
-    0,                                          /* tp_as_async */
-    0,                                          /* tp_repr */
-    0,                                          /* tp_as_number */
-    0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* 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 */
-    0,                                          /* tp_doc */
-    (traverseproc)dequeiter_traverse,           /* tp_traverse */
-    0,                                          /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    PyObject_SelfIter,                          /* tp_iter */
-    (iternextfunc)dequereviter_next,            /* tp_iternext */
-    dequeiter_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 */
-    dequereviter_new,                           /* tp_new */
-    0,
+static PyType_Slot dequereviter_slots[] = {
+    {Py_tp_dealloc, dequeiter_dealloc},
+    {Py_tp_getattro, PyObject_GenericGetAttr},
+    {Py_tp_traverse, dequeiter_traverse},
+    {Py_tp_clear, dequeiter_clear},
+    {Py_tp_iter, PyObject_SelfIter},
+    {Py_tp_iternext, dequereviter_next},
+    {Py_tp_methods, dequeiter_methods},
+    {Py_tp_new, dequereviter_new},
+    {0, NULL},
+};
+
+static PyType_Spec dequereviter_spec = {
+    .name = "collections._deque_reverse_iterator",
+    .basicsize = sizeof(dequeiterobject),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = dequereviter_slots,
 };
 
 /* defaultdict type *********************************************************/
@@ -1939,8 +1934,6 @@ typedef struct {
     PyObject *default_factory;
 } defdictobject;
 
-static PyTypeObject defdict_type; /* Forward */
-
 PyDoc_STRVAR(defdict_missing_doc,
 "__missing__(key) # Called by __getitem__ for missing key; pseudo-code:\n\
   if self.default_factory is None: raise KeyError((key,))\n\
@@ -2071,9 +2064,11 @@ static void
 defdict_dealloc(defdictobject *dd)
 {
     /* bpo-31095: UnTrack is needed before calling any callbacks */
+    PyTypeObject *tp = Py_TYPE(dd);
     PyObject_GC_UnTrack(dd);
     Py_CLEAR(dd->default_factory);
     PyDict_Type.tp_dealloc((PyObject *)dd);
+    Py_DECREF(tp);
 }
 
 static PyObject *
@@ -2117,11 +2112,24 @@ static PyObject*
 defdict_or(PyObject* left, PyObject* right)
 {
     PyObject *self, *other;
-    if (PyObject_TypeCheck(left, &defdict_type)) {
+
+    // Find module state
+    PyTypeObject *tp = Py_TYPE(left);
+    PyObject *mod = PyType_GetModuleByDef(tp, &_collectionsmodule);
+    if (mod == NULL) {
+        PyErr_Clear();
+        tp = Py_TYPE(right);
+        mod = PyType_GetModuleByDef(tp, &_collectionsmodule);
+    }
+    assert(mod != NULL);
+    collections_state *state = get_module_state(mod);
+
+    if (PyObject_TypeCheck(left, state->defdict_type)) {
         self = left;
         other = right;
     }
     else {
+        assert(PyObject_TypeCheck(right, state->defdict_type));
         self = right;
         other = left;
     }
@@ -2141,13 +2149,10 @@ defdict_or(PyObject* left, PyObject* right)
     return new;
 }
 
-static PyNumberMethods defdict_as_number = {
-    .nb_or = defdict_or,
-};
-
 static int
 defdict_traverse(PyObject *self, visitproc visit, void *arg)
 {
+    Py_VISIT(Py_TYPE(self));
     Py_VISIT(((defdictobject *)self)->default_factory);
     return PyDict_Type.tp_traverse(self, visit, arg);
 }
@@ -2203,48 +2208,28 @@ passed to the dict constructor, including keyword arguments.\n\
 /* See comment in xxsubtype.c */
 #define DEFERRED_ADDRESS(ADDR) 0
 
-static PyTypeObject defdict_type = {
-    PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
-    "collections.defaultdict",          /* tp_name */
-    sizeof(defdictobject),              /* tp_basicsize */
-    0,                                  /* tp_itemsize */
-    /* methods */
-    (destructor)defdict_dealloc,        /* tp_dealloc */
-    0,                                  /* tp_vectorcall_offset */
-    0,                                  /* tp_getattr */
-    0,                                  /* tp_setattr */
-    0,                                  /* tp_as_async */
-    (reprfunc)defdict_repr,             /* tp_repr */
-    &defdict_as_number,                 /* tp_as_number */
-    0,                                  /* tp_as_sequence */
-    0,                                  /* tp_as_mapping */
-    0,                                  /* tp_hash */
-    0,                                  /* tp_call */
-    0,                                  /* tp_str */
-    PyObject_GenericGetAttr,            /* tp_getattro */
-    0,                                  /* tp_setattro */
-    0,                                  /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
-                                    /* tp_flags */
-    defdict_doc,                        /* tp_doc */
-    defdict_traverse,                   /* tp_traverse */
-    (inquiry)defdict_tp_clear,          /* tp_clear */
-    0,                                  /* tp_richcompare */
-    0,                                  /* tp_weaklistoffset*/
-    0,                                  /* tp_iter */
-    0,                                  /* tp_iternext */
-    defdict_methods,                    /* tp_methods */
-    defdict_members,                    /* tp_members */
-    0,                                  /* tp_getset */
-    DEFERRED_ADDRESS(&PyDict_Type),     /* tp_base */
-    0,                                  /* tp_dict */
-    0,                                  /* tp_descr_get */
-    0,                                  /* tp_descr_set */
-    0,                                  /* tp_dictoffset */
-    defdict_init,                       /* tp_init */
-    PyType_GenericAlloc,                /* tp_alloc */
-    0,                                  /* tp_new */
-    PyObject_GC_Del,                    /* tp_free */
+static PyType_Slot defdict_slots[] = {
+    {Py_tp_dealloc, defdict_dealloc},
+    {Py_tp_repr, defdict_repr},
+    {Py_nb_or, defdict_or},
+    {Py_tp_getattro, PyObject_GenericGetAttr},
+    {Py_tp_doc, (void *)defdict_doc},
+    {Py_tp_traverse, defdict_traverse},
+    {Py_tp_clear, defdict_tp_clear},
+    {Py_tp_methods, defdict_methods},
+    {Py_tp_members, defdict_members},
+    {Py_tp_init, defdict_init},
+    {Py_tp_alloc, PyType_GenericAlloc},
+    {Py_tp_free, PyObject_GC_Del},
+    {0, NULL},
+};
+
+static PyType_Spec defdict_spec = {
+    .name = "collections.defaultdict",
+    .basicsize = sizeof(defdictobject),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = defdict_slots,
 };
 
 /* helper function for Counter  *********************************************/
@@ -2442,6 +2427,7 @@ static int
 tuplegetter_traverse(PyObject *self, visitproc visit, void *arg)
 {
     _tuplegetterobject *tuplegetter = (_tuplegetterobject *)self;
+    Py_VISIT(Py_TYPE(tuplegetter));
     Py_VISIT(tuplegetter->doc);
     return 0;
 }
@@ -2457,9 +2443,11 @@ tuplegetter_clear(PyObject *self)
 static void
 tuplegetter_dealloc(_tuplegetterobject *self)
 {
+    PyTypeObject *tp = Py_TYPE(self);
     PyObject_GC_UnTrack(self);
     tuplegetter_clear((PyObject*)self);
-    Py_TYPE(self)->tp_free((PyObject*)self);
+    tp->tp_free((PyObject*)self);
+    Py_DECREF(tp);
 }
 
 static PyObject*
@@ -2487,52 +2475,60 @@ static PyMethodDef tuplegetter_methods[] = {
     {NULL},
 };
 
-static PyTypeObject tuplegetter_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "_collections._tuplegetter",                /* tp_name */
-    sizeof(_tuplegetterobject),                 /* tp_basicsize */
-    0,                                          /* tp_itemsize */
-    /* methods */
-    (destructor)tuplegetter_dealloc,            /* tp_dealloc */
-    0,                                          /* tp_vectorcall_offset */
-    0,                                          /* tp_getattr */
-    0,                                          /* tp_setattr */
-    0,                                          /* tp_as_async */
-    (reprfunc)tuplegetter_repr,                 /* tp_repr */
-    0,                                          /* tp_as_number */
-    0,                                          /* tp_as_sequence */
-    0,                                          /* tp_as_mapping */
-    0,                                          /* tp_hash */
-    0,                                          /* tp_call */
-    0,                                          /* tp_str */
-    0,                                          /* tp_getattro */
-    0,                                          /* tp_setattro */
-    0,                                          /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,    /* tp_flags */
-    0,                                          /* tp_doc */
-    (traverseproc)tuplegetter_traverse,         /* tp_traverse */
-    (inquiry)tuplegetter_clear,                 /* tp_clear */
-    0,                                          /* tp_richcompare */
-    0,                                          /* tp_weaklistoffset */
-    0,                                          /* tp_iter */
-    0,                                          /* tp_iternext */
-    tuplegetter_methods,                        /* tp_methods */
-    tuplegetter_members,                        /* tp_members */
-    0,                                          /* tp_getset */
-    0,                                          /* tp_base */
-    0,                                          /* tp_dict */
-    tuplegetter_descr_get,                      /* tp_descr_get */
-    tuplegetter_descr_set,                      /* tp_descr_set */
-    0,                                          /* tp_dictoffset */
-    0,                                          /* tp_init */
-    0,                                          /* tp_alloc */
-    tuplegetter_new,                            /* tp_new */
-    0,
+static PyType_Slot tuplegetter_slots[] = {
+    {Py_tp_dealloc, tuplegetter_dealloc},
+    {Py_tp_repr, tuplegetter_repr},
+    {Py_tp_traverse, tuplegetter_traverse},
+    {Py_tp_clear, tuplegetter_clear},
+    {Py_tp_methods, tuplegetter_methods},
+    {Py_tp_members, tuplegetter_members},
+    {Py_tp_descr_get, tuplegetter_descr_get},
+    {Py_tp_descr_set, tuplegetter_descr_set},
+    {Py_tp_new, tuplegetter_new},
+    {0, NULL},
+};
+
+static PyType_Spec tuplegetter_spec = {
+    .name = "collections._tuplegetter",
+    .basicsize = sizeof(_tuplegetterobject),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = tuplegetter_slots,
 };
 
 
 /* module level code ********************************************************/
 
+static int
+collections_traverse(PyObject *mod, visitproc visit, void *arg)
+{
+    collections_state *state = get_module_state(mod);
+    Py_VISIT(state->deque_type);
+    Py_VISIT(state->defdict_type);
+    Py_VISIT(state->dequeiter_type);
+    Py_VISIT(state->dequereviter_type);
+    Py_VISIT(state->tuplegetter_type);
+    return 0;
+}
+
+static int
+collections_clear(PyObject *mod)
+{
+    collections_state *state = get_module_state(mod);
+    Py_CLEAR(state->deque_type);
+    Py_CLEAR(state->defdict_type);
+    Py_CLEAR(state->dequeiter_type);
+    Py_CLEAR(state->dequereviter_type);
+    Py_CLEAR(state->tuplegetter_type);
+    return 0;
+}
+
+static void
+collections_free(void *module)
+{
+    collections_clear((PyObject *)module);
+}
+
 PyDoc_STRVAR(collections_doc,
 "High performance data structures.\n\
 - deque:        ordered collection accessible from endpoints only\n\
@@ -2544,43 +2540,50 @@ static struct PyMethodDef collections_methods[] = {
     {NULL,       NULL}          /* sentinel */
 };
 
+#define ADD_TYPE(MOD, SPEC, TYPE, BASE) do {                        \
+    TYPE = (PyTypeObject *)PyType_FromMetaclass(NULL, MOD, SPEC,    \
+                                                (PyObject *)BASE);  \
+    if (TYPE == NULL) {                                             \
+        return -1;                                                  \
+    }                                                               \
+    if (PyModule_AddType(MOD, TYPE) < 0) {                          \
+        return -1;                                                  \
+    }                                                               \
+} while (0)
+
 static int
 collections_exec(PyObject *module) {
-    PyTypeObject *typelist[] = {
-        &deque_type,
-        &defdict_type,
-        &PyODict_Type,
-        &dequeiter_type,
-        &dequereviter_type,
-        &tuplegetter_type
-    };
-
-    defdict_type.tp_base = &PyDict_Type;
-
-    for (size_t i = 0; i < Py_ARRAY_LENGTH(typelist); i++) {
-        if (PyModule_AddType(module, typelist[i]) < 0) {
-            return -1;
-        }
+    collections_state *state = get_module_state(module);
+    ADD_TYPE(module, &deque_spec, state->deque_type, NULL);
+    ADD_TYPE(module, &defdict_spec, state->defdict_type, &PyDict_Type);
+    ADD_TYPE(module, &dequeiter_spec, state->dequeiter_type, NULL);
+    ADD_TYPE(module, &dequereviter_spec, state->dequereviter_type, NULL);
+    ADD_TYPE(module, &tuplegetter_spec, state->tuplegetter_type, NULL);
+
+    if (PyModule_AddType(module, &PyODict_Type) < 0) {
+        return -1;
     }
 
     return 0;
 }
 
+#undef ADD_TYPE
+
 static struct PyModuleDef_Slot collections_slots[] = {
     {Py_mod_exec, collections_exec},
     {0, NULL}
 };
 
 static struct PyModuleDef _collectionsmodule = {
-    PyModuleDef_HEAD_INIT,
-    "_collections",
-    collections_doc,
-    0,
-    collections_methods,
-    collections_slots,
-    NULL,
-    NULL,
-    NULL
+    .m_base = PyModuleDef_HEAD_INIT,
+    .m_name = "_collections",
+    .m_doc = collections_doc,
+    .m_size = sizeof(collections_state),
+    .m_methods = collections_methods,
+    .m_slots = collections_slots,
+    .m_traverse = collections_traverse,
+    .m_clear = collections_clear,
+    .m_free = collections_free,
 };
 
 PyMODINIT_FUNC
index 8ea0255b0610706d884e67b4198d70b3df9f2d27..3882d069216e28abe780abcfbe30dba599dacd99 100644 (file)
@@ -46,7 +46,7 @@ static PyObject *
 tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
 {
     PyObject *return_value = NULL;
-    PyTypeObject *base_tp = &tuplegetter_type;
+    PyTypeObject *base_tp = clinic_state()->tuplegetter_type;
     Py_ssize_t index;
     PyObject *doc;
 
@@ -75,4 +75,4 @@ tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=91a0f221c7b1f96c input=a9049054013a1b77]*/
+/*[clinic end generated code: output=00e516317d2b8bed input=a9049054013a1b77]*/
index 823340ecc491883a58dc92c5c61a0e9162251045..5c173b1041e3e452629f00ebac637bcb489de866 100644 (file)
@@ -316,11 +316,6 @@ Python/instrumentation.c   -       _PyInstrumentation_MISSING      -
 ##-----------------------
 ## static types
 
-Modules/_collectionsmodule.c   -       defdict_type    -
-Modules/_collectionsmodule.c   -       deque_type      -
-Modules/_collectionsmodule.c   -       dequeiter_type  -
-Modules/_collectionsmodule.c   -       dequereviter_type       -
-Modules/_collectionsmodule.c   -       tuplegetter_type        -
 Modules/_io/bufferedio.c       -       PyBufferedIOBase_Type   -
 Modules/_io/bytesio.c  -       _PyBytesIOBuffer_Type   -
 Modules/_io/iobase.c   -       PyIOBase_Type   -