[Python-checkins] bpo-35381 Remove all static state from posixmodule (GH-15892)

Miss Islington (bot) webhook-mailer at python.org
Tue Nov 5 10:59:29 EST 2019


https://github.com/python/cpython/commit/b3966639d28313809774ca3859a347b9007be8d2
commit: b3966639d28313809774ca3859a347b9007be8d2
branch: master
author: Eddie Elizondo <eelizondo at fb.com>
committer: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
date: 2019-11-05T07:16:14-08:00
summary:

bpo-35381 Remove all static state from posixmodule (GH-15892)



After #9665, this moves the remaining types in posixmodule to be heap-allocated to make it compatible with PEP384 as well as modifying all the type accessors to fully make the type opaque.

The original PR that got messed up a rebase: https://github.com/python/cpython/pull/10854. All the issues in that commit have now been addressed since https://github.com/python/cpython/pull/11661 got committed.

This change also removes any state from the data segment and onto the module state itself.


https://bugs.python.org/issue35381



Automerge-Triggered-By: @encukou

files:
A Misc/NEWS.d/next/C API/2019-01-18-17-05-26.bpo-35381.9CbeW3.rst
M Lib/test/test_os.py
M Modules/clinic/posixmodule.c.h
M Modules/posixmodule.c

diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index d2bd9c2f232f1..bf40cb1e8fa7d 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -3638,6 +3638,24 @@ def test_os_all(self):
         self.assertIn('walk', os.__all__)
 
 
+class TestDirEntry(unittest.TestCase):
+    def setUp(self):
+        self.path = os.path.realpath(support.TESTFN)
+        self.addCleanup(support.rmtree, self.path)
+        os.mkdir(self.path)
+
+    def test_uninstantiable(self):
+        self.assertRaises(TypeError, os.DirEntry)
+
+    def test_unpickable(self):
+        filename = create_file(os.path.join(self.path, "file.txt"), b'python')
+        entry = [entry for entry in os.scandir(self.path)].pop()
+        self.assertIsInstance(entry, os.DirEntry)
+        self.assertEqual(entry.name, "file.txt")
+        import pickle
+        self.assertRaises(TypeError, pickle.dumps, entry, filename)
+
+
 class TestScandir(unittest.TestCase):
     check_no_resource_warning = support.check_no_resource_warning
 
@@ -3672,6 +3690,18 @@ def assert_stat_equal(self, stat1, stat2, skip_fields):
         else:
             self.assertEqual(stat1, stat2)
 
+    def test_uninstantiable(self):
+        scandir_iter = os.scandir(self.path)
+        self.assertRaises(TypeError, type(scandir_iter))
+        scandir_iter.close()
+
+    def test_unpickable(self):
+        filename = self.create_file("file.txt")
+        scandir_iter = os.scandir(self.path)
+        import pickle
+        self.assertRaises(TypeError, pickle.dumps, scandir_iter, filename)
+        scandir_iter.close()
+
     def check_entry(self, entry, name, is_dir, is_file, is_symlink):
         self.assertIsInstance(entry, os.DirEntry)
         self.assertEqual(entry.name, name)
diff --git a/Misc/NEWS.d/next/C API/2019-01-18-17-05-26.bpo-35381.9CbeW3.rst b/Misc/NEWS.d/next/C API/2019-01-18-17-05-26.bpo-35381.9CbeW3.rst
new file mode 100644
index 0000000000000..a7efce468c6d8
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2019-01-18-17-05-26.bpo-35381.9CbeW3.rst	
@@ -0,0 +1,2 @@
+Convert posixmodule.c statically allocated types ``DirEntryType`` and
+``ScandirIteratorType`` to heap-allocated types.
diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h
index 561cbb0ca82ab..3dada674fbab0 100644
--- a/Modules/clinic/posixmodule.c.h
+++ b/Modules/clinic/posixmodule.c.h
@@ -2838,7 +2838,7 @@ PyDoc_STRVAR(os_sched_param__doc__,
 "sched_param(sched_priority)\n"
 "--\n"
 "\n"
-"Current has only one field: sched_priority\");\n"
+"Currently has only one field: sched_priority\n"
 "\n"
 "  sched_priority\n"
 "    A scheduling parameter.");
@@ -8731,4 +8731,4 @@ os__remove_dll_directory(PyObject *module, PyObject *const *args, Py_ssize_t nar
 #ifndef OS__REMOVE_DLL_DIRECTORY_METHODDEF
     #define OS__REMOVE_DLL_DIRECTORY_METHODDEF
 #endif /* !defined(OS__REMOVE_DLL_DIRECTORY_METHODDEF) */
-/*[clinic end generated code: output=fe7897441fed5402 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=c6e67d475eef00c4 input=a9049054013a1b77]*/
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index dcd90d3a5152d..6d837c6552f49 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -217,6 +217,7 @@ corresponding Unix manual entries for more information on calls.");
 #endif  /* _MSC_VER */
 #endif  /* ! __WATCOMC__ || __QNX__ */
 
+_Py_IDENTIFIER(__fspath__);
 
 /*[clinic input]
 # one of the few times we lie about this name!
@@ -537,7 +538,7 @@ _Py_Uid_Converter(PyObject *obj, void *p)
     if (index == NULL) {
         PyErr_Format(PyExc_TypeError,
                      "uid should be integer, not %.200s",
-                     Py_TYPE(obj)->tp_name);
+                     _PyType_Name(Py_TYPE(obj)));
         return 0;
     }
 
@@ -643,7 +644,7 @@ _Py_Gid_Converter(PyObject *obj, void *p)
     if (index == NULL) {
         PyErr_Format(PyExc_TypeError,
                      "gid should be integer, not %.200s",
-                     Py_TYPE(obj)->tp_name);
+                     _PyType_Name(Py_TYPE(obj)));
         return 0;
     }
 
@@ -810,11 +811,37 @@ dir_fd_converter(PyObject *o, void *p)
     else {
         PyErr_Format(PyExc_TypeError,
                      "argument should be integer or None, not %.200s",
-                     Py_TYPE(o)->tp_name);
+                     _PyType_Name(Py_TYPE(o)));
         return 0;
     }
 }
 
+typedef struct {
+    PyObject *billion;
+    PyObject *posix_putenv_garbage;
+    PyObject *DirEntryType;
+    PyObject *ScandirIteratorType;
+#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM)
+    PyObject *SchedParamType;
+#endif
+    PyObject *StatResultType;
+    PyObject *StatVFSResultType;
+    PyObject *TerminalSizeType;
+    PyObject *TimesResultType;
+    PyObject *UnameResultType;
+#if defined(HAVE_WAITID) && !defined(__APPLE__)
+    PyObject *WaitidResultType;
+#endif
+#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
+    PyObject *struct_rusage;
+#endif
+    PyObject *st_mode;
+} _posixstate;
+
+static struct PyModuleDef posixmodule;
+
+#define _posixstate(o) ((_posixstate *)PyModule_GetState(o))
+#define _posixstate_global ((_posixstate *)PyModule_GetState(PyState_FindModule(&posixmodule)))
 
 /*
  * A PyArg_ParseTuple "converter" function
@@ -984,7 +1011,6 @@ path_converter(PyObject *o, void *p)
 
     if (!is_index && !is_buffer && !is_unicode && !is_bytes) {
         /* Inline PyOS_FSPath() for better error messages. */
-        _Py_IDENTIFIER(__fspath__);
         PyObject *func, *res;
 
         func = _PyObject_LookupSpecial(o, &PyId___fspath__);
@@ -1005,8 +1031,8 @@ path_converter(PyObject *o, void *p)
         else {
             PyErr_Format(PyExc_TypeError,
                  "expected %.200s.__fspath__() to return str or bytes, "
-                 "not %.200s", Py_TYPE(o)->tp_name,
-                 Py_TYPE(res)->tp_name);
+                 "not %.200s", _PyType_Name(Py_TYPE(o)),
+                 _PyType_Name(Py_TYPE(res)));
             Py_DECREF(res);
             goto error_exit;
         }
@@ -1058,7 +1084,7 @@ path_converter(PyObject *o, void *p)
             path->allow_fd ? "string, bytes, os.PathLike or integer" :
             path->nullable ? "string, bytes, os.PathLike or None" :
                              "string, bytes or os.PathLike",
-            Py_TYPE(o)->tp_name)) {
+            _PyType_Name(Py_TYPE(o)))) {
             goto error_exit;
         }
         bytes = PyBytes_FromObject(o);
@@ -1089,7 +1115,7 @@ path_converter(PyObject *o, void *p)
             path->allow_fd ? "string, bytes, os.PathLike or integer" :
             path->nullable ? "string, bytes, os.PathLike or None" :
                              "string, bytes or os.PathLike",
-            Py_TYPE(o)->tp_name);
+            _PyType_Name(Py_TYPE(o)));
         goto error_exit;
     }
 
@@ -2047,14 +2073,6 @@ static PyStructSequence_Desc waitid_result_desc = {
     waitid_result_fields,
     5
 };
-static PyTypeObject* WaitidResultType;
-#endif
-
-static int initialized;
-static PyTypeObject* StatResultType;
-static PyTypeObject* StatVFSResultType;
-#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM)
-static PyTypeObject* SchedParamType;
 #endif
 static newfunc structseq_new;
 
@@ -2080,8 +2098,61 @@ statresult_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     return (PyObject*)result;
 }
 
+static int
+_posix_clear(PyObject *module)
+{
+    Py_CLEAR(_posixstate(module)->billion);
+    Py_CLEAR(_posixstate(module)->posix_putenv_garbage);
+    Py_CLEAR(_posixstate(module)->DirEntryType);
+    Py_CLEAR(_posixstate(module)->ScandirIteratorType);
+#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM)
+    Py_CLEAR(_posixstate(module)->SchedParamType);
+#endif
+    Py_CLEAR(_posixstate(module)->StatResultType);
+    Py_CLEAR(_posixstate(module)->StatVFSResultType);
+    Py_CLEAR(_posixstate(module)->TerminalSizeType);
+    Py_CLEAR(_posixstate(module)->TimesResultType);
+    Py_CLEAR(_posixstate(module)->UnameResultType);
+#if defined(HAVE_WAITID) && !defined(__APPLE__)
+    Py_CLEAR(_posixstate(module)->WaitidResultType);
+#endif
+#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
+    Py_CLEAR(_posixstate(module)->struct_rusage);
+#endif
+    Py_CLEAR(_posixstate(module)->st_mode);
+    return 0;
+}
 
-static PyObject *billion = NULL;
+static int
+_posix_traverse(PyObject *module, visitproc visit, void *arg)
+{
+    Py_VISIT(_posixstate(module)->billion);
+    Py_VISIT(_posixstate(module)->posix_putenv_garbage);
+    Py_VISIT(_posixstate(module)->DirEntryType);
+    Py_VISIT(_posixstate(module)->ScandirIteratorType);
+#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM)
+    Py_VISIT(_posixstate(module)->SchedParamType);
+#endif
+    Py_VISIT(_posixstate(module)->StatResultType);
+    Py_VISIT(_posixstate(module)->StatVFSResultType);
+    Py_VISIT(_posixstate(module)->TerminalSizeType);
+    Py_VISIT(_posixstate(module)->TimesResultType);
+    Py_VISIT(_posixstate(module)->UnameResultType);
+#if defined(HAVE_WAITID) && !defined(__APPLE__)
+    Py_VISIT(_posixstate(module)->WaitidResultType);
+#endif
+#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
+    Py_VISIT(_posixstate(module)->struct_rusage);
+#endif
+    Py_VISIT(_posixstate(module)->st_mode);
+    return 0;
+}
+
+static void
+_posix_free(void *module)
+{
+   _posix_clear((PyObject *)module);
+}
 
 static void
 fill_time(PyObject *v, int index, time_t sec, unsigned long nsec)
@@ -2095,7 +2166,7 @@ fill_time(PyObject *v, int index, time_t sec, unsigned long nsec)
     if (!(s && ns_fractional))
         goto exit;
 
-    s_in_ns = PyNumber_Multiply(s, billion);
+    s_in_ns = PyNumber_Multiply(s, _posixstate_global->billion);
     if (!s_in_ns)
         goto exit;
 
@@ -2128,7 +2199,8 @@ static PyObject*
 _pystat_fromstructstat(STRUCT_STAT *st)
 {
     unsigned long ansec, mnsec, cnsec;
-    PyObject *v = PyStructSequence_New(StatResultType);
+    PyObject *StatResultType = _posixstate_global->StatResultType;
+    PyObject *v = PyStructSequence_New((PyTypeObject *)StatResultType);
     if (v == NULL)
         return NULL;
 
@@ -4505,15 +4577,12 @@ or via the attributes sysname, nodename, release, version, and machine.\n\
 See os.uname for more information.");
 
 static PyStructSequence_Desc uname_result_desc = {
-    "uname_result", /* name */
+    MODNAME ".uname_result", /* name */
     uname_result__doc__, /* doc */
     uname_result_fields,
     5
 };
 
-static PyTypeObject* UnameResultType;
-
-
 #ifdef HAVE_UNAME
 /*[clinic input]
 os.uname
@@ -4539,7 +4608,8 @@ os_uname_impl(PyObject *module)
     if (res < 0)
         return posix_error();
 
-    value = PyStructSequence_New(UnameResultType);
+    PyObject *UnameResultType = _posixstate(module)->UnameResultType;
+    value = PyStructSequence_New((PyTypeObject *)UnameResultType);
     if (value == NULL)
         return NULL;
 
@@ -4720,13 +4790,13 @@ split_py_long_to_s_and_ns(PyObject *py_long, time_t *s, long *ns)
 {
     int result = 0;
     PyObject *divmod;
-    divmod = PyNumber_Divmod(py_long, billion);
+    divmod = PyNumber_Divmod(py_long, _posixstate_global->billion);
     if (!divmod)
         goto exit;
     if (!PyTuple_Check(divmod) || PyTuple_GET_SIZE(divmod) != 2) {
         PyErr_Format(PyExc_TypeError,
                      "%.200s.__divmod__() must return a 2-tuple, not %.200s",
-                     Py_TYPE(py_long)->tp_name, Py_TYPE(divmod)->tp_name);
+                     _PyType_Name(Py_TYPE(py_long)), _PyType_Name(Py_TYPE(divmod)));
         goto exit;
     }
     *s = _PyLong_AsTime_t(PyTuple_GET_ITEM(divmod, 0));
@@ -5973,7 +6043,7 @@ check_null_or_callable(PyObject *obj, const char* obj_name)
 {
     if (obj && !PyCallable_Check(obj)) {
         PyErr_Format(PyExc_TypeError, "'%s' must be callable, not %s",
-                     obj_name, Py_TYPE(obj)->tp_name);
+                     obj_name, _PyType_Name(Py_TYPE(obj)));
         return -1;
     }
     return 0;
@@ -6177,12 +6247,12 @@ os.sched_param.__new__
     sched_priority: object
         A scheduling parameter.
 
-Current has only one field: sched_priority");
+Currently has only one field: sched_priority
 [clinic start generated code]*/
 
 static PyObject *
 os_sched_param_impl(PyTypeObject *type, PyObject *sched_priority)
-/*[clinic end generated code: output=48f4067d60f48c13 input=ab4de35a9a7811f2]*/
+/*[clinic end generated code: output=48f4067d60f48c13 input=eb42909a2c0e3e6c]*/
 {
     PyObject *res;
 
@@ -6194,7 +6264,6 @@ os_sched_param_impl(PyTypeObject *type, PyObject *sched_priority)
     return res;
 }
 
-
 PyDoc_VAR(os_sched_param__doc__);
 
 static PyStructSequence_Field sched_param_fields[] = {
@@ -6214,7 +6283,8 @@ convert_sched_param(PyObject *param, struct sched_param *res)
 {
     long priority;
 
-    if (Py_TYPE(param) != SchedParamType) {
+    PyObject *SchedParamType = _posixstate_global->SchedParamType;
+    if (Py_TYPE(param) != (PyTypeObject *)SchedParamType) {
         PyErr_SetString(PyExc_TypeError, "must have a sched_param object");
         return 0;
     }
@@ -6285,7 +6355,8 @@ os_sched_getparam_impl(PyObject *module, pid_t pid)
 
     if (sched_getparam(pid, &param))
         return posix_error();
-    result = PyStructSequence_New(SchedParamType);
+    PyObject *SchedParamType = _posixstate_global->SchedParamType;
+    result = PyStructSequence_New((PyTypeObject *)SchedParamType);
     if (!result)
         return NULL;
     priority = PyLong_FromLong(param.sched_priority);
@@ -7494,8 +7565,7 @@ static PyObject *
 wait_helper(pid_t pid, int status, struct rusage *ru)
 {
     PyObject *result;
-    static PyObject *struct_rusage;
-    _Py_IDENTIFIER(struct_rusage);
+    PyObject *struct_rusage;
 
     if (pid == -1)
         return posix_error();
@@ -7506,15 +7576,13 @@ wait_helper(pid_t pid, int status, struct rusage *ru)
         memset(ru, 0, sizeof(*ru));
     }
 
-    if (struct_rusage == NULL) {
-        PyObject *m = PyImport_ImportModuleNoBlock("resource");
-        if (m == NULL)
-            return NULL;
-        struct_rusage = _PyObject_GetAttrId(m, &PyId_struct_rusage);
-        Py_DECREF(m);
-        if (struct_rusage == NULL)
-            return NULL;
-    }
+    PyObject *m = PyImport_ImportModuleNoBlock("resource");
+    if (m == NULL)
+        return NULL;
+    struct_rusage = PyObject_GetAttr(m, _posixstate_global->struct_rusage);
+    Py_DECREF(m);
+    if (struct_rusage == NULL)
+        return NULL;
 
     /* XXX(nnorwitz): Copied (w/mods) from resource.c, there should be only one. */
     result = PyStructSequence_New((PyTypeObject*) struct_rusage);
@@ -7668,7 +7736,8 @@ os_waitid_impl(PyObject *module, idtype_t idtype, id_t id, int options)
     if (si.si_pid == 0)
         Py_RETURN_NONE;
 
-    result = PyStructSequence_New(WaitidResultType);
+    PyObject *WaitidResultType = _posixstate(module)->WaitidResultType;
+    result = PyStructSequence_New((PyTypeObject *)WaitidResultType);
     if (!result)
         return NULL;
 
@@ -8119,8 +8188,6 @@ static PyStructSequence_Desc times_result_desc = {
     5
 };
 
-static PyTypeObject* TimesResultType;
-
 #ifdef MS_WINDOWS
 #define HAVE_TIMES  /* mandatory, for the method table */
 #endif
@@ -8132,7 +8199,8 @@ build_times_result(double user, double system,
     double children_user, double children_system,
     double elapsed)
 {
-    PyObject *value = PyStructSequence_New(TimesResultType);
+    PyObject *TimesResultType = _posixstate_global->TimesResultType;
+    PyObject *value = PyStructSequence_New((PyTypeObject *)TimesResultType);
     if (value == NULL)
         return NULL;
 
@@ -9953,10 +10021,6 @@ os_posix_fadvise_impl(PyObject *module, int fd, Py_off_t offset,
 
 #ifdef HAVE_PUTENV
 
-/* Save putenv() parameters as values here, so we can collect them when they
- * get re-set with another call for the same key. */
-static PyObject *posix_putenv_garbage;
-
 static void
 posix_putenv_garbage_setitem(PyObject *name, PyObject *value)
 {
@@ -9964,7 +10028,7 @@ posix_putenv_garbage_setitem(PyObject *name, PyObject *value)
      * this will cause previous value to be collected.  This has to
      * happen after the real putenv() call because the old value
      * was still accessible until then. */
-    if (PyDict_SetItem(posix_putenv_garbage, name, value))
+    if (PyDict_SetItem(_posixstate_global->posix_putenv_garbage, name, value))
         /* really not much we can do; just leak */
         PyErr_Clear();
     else
@@ -10101,7 +10165,7 @@ os_unsetenv_impl(PyObject *module, PyObject *name)
      * happen after the real unsetenv() call because the
      * old value was still accessible until then.
      */
-    if (PyDict_DelItem(posix_putenv_garbage, name)) {
+    if (PyDict_DelItem(_posixstate(module)->posix_putenv_garbage, name)) {
         /* really not much we can do; just leak */
         if (!PyErr_ExceptionMatches(PyExc_KeyError)) {
             return NULL;
@@ -10312,7 +10376,8 @@ os_WSTOPSIG_impl(PyObject *module, int status)
 
 static PyObject*
 _pystatvfs_fromstructstatvfs(struct statvfs st) {
-    PyObject *v = PyStructSequence_New(StatVFSResultType);
+    PyObject *StatVFSResultType = _posixstate_global->StatVFSResultType;
+    PyObject *v = PyStructSequence_New((PyTypeObject *)StatVFSResultType);
     if (v == NULL)
         return NULL;
 
@@ -12089,8 +12154,6 @@ os_memfd_create_impl(PyObject *module, PyObject *name, unsigned int flags)
 
 /* Terminal size querying */
 
-static PyTypeObject* TerminalSizeType;
-
 PyDoc_STRVAR(TerminalSize_docstring,
     "A tuple of (columns, lines) for holding terminal window size");
 
@@ -12181,7 +12244,8 @@ get_terminal_size(PyObject *self, PyObject *args)
     }
 #endif /* TERMSIZE_USE_CONIO */
 
-    termsize = PyStructSequence_New(TerminalSizeType);
+    PyObject *TerminalSizeType = _posixstate(self)->TerminalSizeType;
+    termsize = PyStructSequence_New((PyTypeObject *)TerminalSizeType);
     if (termsize == NULL)
         return NULL;
     PyStructSequence_SET_ITEM(termsize, 0, PyLong_FromLong(columns));
@@ -12379,9 +12443,9 @@ os_set_blocking_impl(PyObject *module, int fd, int blocking)
 
 
 /*[clinic input]
-class os.DirEntry "DirEntry *" "&DirEntryType"
+class os.DirEntry "DirEntry *" "DirEntryType"
 [clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=3138f09f7c683f1d]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=3c18c7a448247980]*/
 
 typedef struct {
     PyObject_HEAD
@@ -12402,14 +12466,25 @@ typedef struct {
 #endif
 } DirEntry;
 
+static PyObject *
+_disabled_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+    PyErr_Format(PyExc_TypeError,
+        "cannot create '%.100s' instances", _PyType_Name(type));
+    return NULL;
+}
+
 static void
 DirEntry_dealloc(DirEntry *entry)
 {
+    PyTypeObject *tp = Py_TYPE(entry);
     Py_XDECREF(entry->name);
     Py_XDECREF(entry->path);
     Py_XDECREF(entry->stat);
     Py_XDECREF(entry->lstat);
-    Py_TYPE(entry)->tp_free((PyObject *)entry);
+    freefunc free_func = PyType_GetSlot(tp, Py_tp_free);
+    free_func(entry);
+    Py_DECREF(tp);
 }
 
 /* Forward reference */
@@ -12538,7 +12613,6 @@ DirEntry_test_mode(DirEntry *self, int follow_symlinks, unsigned short mode_bits
 #ifdef MS_WINDOWS
     unsigned long dir_bits;
 #endif
-    _Py_IDENTIFIER(st_mode);
 
 #ifdef MS_WINDOWS
     is_symlink = (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK;
@@ -12561,7 +12635,7 @@ DirEntry_test_mode(DirEntry *self, int follow_symlinks, unsigned short mode_bits
             }
             goto error;
         }
-        st_mode = _PyObject_GetAttrId(stat, &PyId_st_mode);
+        st_mode = PyObject_GetAttr(stat, _posixstate_global->st_mode);
         if (!st_mode)
             goto error;
 
@@ -12709,39 +12783,24 @@ static PyMethodDef DirEntry_methods[] = {
     {NULL}
 };
 
-static PyTypeObject DirEntryType = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    MODNAME ".DirEntry",                    /* tp_name */
-    sizeof(DirEntry),                       /* tp_basicsize */
-    0,                                      /* tp_itemsize */
-    /* methods */
-    (destructor)DirEntry_dealloc,           /* tp_dealloc */
-    0,                                      /* tp_vectorcall_offset */
-    0,                                      /* tp_getattr */
-    0,                                      /* tp_setattr */
-    0,                                      /* tp_as_async */
-    (reprfunc)DirEntry_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,                     /* tp_flags */
-    0,                                      /* tp_doc */
-    0,                                      /* tp_traverse */
-    0,                                      /* tp_clear */
-    0,                                      /* tp_richcompare */
-    0,                                      /* tp_weaklistoffset */
-    0,                                      /* tp_iter */
-    0,                                      /* tp_iternext */
-    DirEntry_methods,                       /* tp_methods */
-    DirEntry_members,                       /* tp_members */
+static PyType_Slot DirEntryType_slots[] = {
+    {Py_tp_new, _disabled_new},
+    {Py_tp_dealloc, DirEntry_dealloc},
+    {Py_tp_repr, DirEntry_repr},
+    {Py_tp_methods, DirEntry_methods},
+    {Py_tp_members, DirEntry_members},
+    {0, 0},
+};
+
+static PyType_Spec DirEntryType_spec = {
+    MODNAME ".DirEntry",
+    sizeof(DirEntry),
+    0,
+    Py_TPFLAGS_DEFAULT,
+    DirEntryType_slots
 };
 
+
 #ifdef MS_WINDOWS
 
 static wchar_t *
@@ -12785,7 +12844,8 @@ DirEntry_from_find_data(path_t *path, WIN32_FIND_DATAW *dataW)
     ULONG reparse_tag;
     wchar_t *joined_path;
 
-    entry = PyObject_New(DirEntry, &DirEntryType);
+    PyObject *DirEntryType = _posixstate_global->DirEntryType;
+    entry = PyObject_New(DirEntry, (PyTypeObject *)DirEntryType);
     if (!entry)
         return NULL;
     entry->name = NULL;
@@ -12872,7 +12932,8 @@ DirEntry_from_posix_info(path_t *path, const char *name, Py_ssize_t name_len,
     DirEntry *entry;
     char *joined_path;
 
-    entry = PyObject_New(DirEntry, &DirEntryType);
+    PyObject *DirEntryType = _posixstate_global->DirEntryType;
+    entry = PyObject_New(DirEntry, (PyTypeObject *)DirEntryType);
     if (!entry)
         return NULL;
     entry->name = NULL;
@@ -13134,10 +13195,13 @@ ScandirIterator_finalize(ScandirIterator *iterator)
 static void
 ScandirIterator_dealloc(ScandirIterator *iterator)
 {
+    PyTypeObject *tp = Py_TYPE(iterator);
     if (PyObject_CallFinalizerFromDealloc((PyObject *)iterator) < 0)
         return;
 
-    Py_TYPE(iterator)->tp_free((PyObject *)iterator);
+    freefunc free_func = PyType_GetSlot(tp, Py_tp_free);
+    free_func(iterator);
+    Py_DECREF(tp);
 }
 
 static PyMethodDef ScandirIterator_methods[] = {
@@ -13147,56 +13211,22 @@ static PyMethodDef ScandirIterator_methods[] = {
     {NULL}
 };
 
-static PyTypeObject ScandirIteratorType = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    MODNAME ".ScandirIterator",             /* tp_name */
-    sizeof(ScandirIterator),                /* tp_basicsize */
-    0,                                      /* tp_itemsize */
-    /* methods */
-    (destructor)ScandirIterator_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 */
-    0,                                      /* tp_getattro */
-    0,                                      /* tp_setattro */
-    0,                                      /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT,                     /* tp_flags */
-    0,                                      /* tp_doc */
-    0,                                      /* tp_traverse */
-    0,                                      /* tp_clear */
-    0,                                      /* tp_richcompare */
-    0,                                      /* tp_weaklistoffset */
-    PyObject_SelfIter,                      /* tp_iter */
-    (iternextfunc)ScandirIterator_iternext, /* tp_iternext */
-    ScandirIterator_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 */
-    0,                                      /* tp_new */
-    0,                                      /* tp_free */
-    0,                                      /* tp_is_gc */
-    0,                                      /* tp_bases */
-    0,                                      /* tp_mro */
-    0,                                      /* tp_cache */
-    0,                                      /* tp_subclasses */
-    0,                                      /* tp_weaklist */
-    0,                                      /* tp_del */
-    0,                                      /* tp_version_tag */
-    (destructor)ScandirIterator_finalize,   /* tp_finalize */
+static PyType_Slot ScandirIteratorType_slots[] = {
+    {Py_tp_new, _disabled_new},
+    {Py_tp_dealloc, ScandirIterator_dealloc},
+    {Py_tp_finalize, ScandirIterator_finalize},
+    {Py_tp_iter, PyObject_SelfIter},
+    {Py_tp_iternext, ScandirIterator_iternext},
+    {Py_tp_methods, ScandirIterator_methods},
+    {0, 0},
+};
+
+static PyType_Spec ScandirIteratorType_spec = {
+    MODNAME ".ScandirIterator",
+    sizeof(ScandirIterator),
+    0,
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_FINALIZE,
+    ScandirIteratorType_slots
 };
 
 /*[clinic input]
@@ -13232,7 +13262,8 @@ os_scandir_impl(PyObject *module, path_t *path)
         return NULL;
     }
 
-    iterator = PyObject_New(ScandirIterator, &ScandirIteratorType);
+    PyObject *ScandirIteratorType = _posixstate(module)->ScandirIteratorType;
+    iterator = PyObject_New(ScandirIterator, (PyTypeObject *)ScandirIteratorType);
     if (!iterator)
         return NULL;
 
@@ -13322,7 +13353,6 @@ PyOS_FSPath(PyObject *path)
 {
     /* For error message reasons, this function is manually inlined in
        path_converter(). */
-    _Py_IDENTIFIER(__fspath__);
     PyObject *func = NULL;
     PyObject *path_repr = NULL;
 
@@ -13336,7 +13366,7 @@ PyOS_FSPath(PyObject *path)
         return PyErr_Format(PyExc_TypeError,
                             "expected str, bytes or os.PathLike object, "
                             "not %.200s",
-                            Py_TYPE(path)->tp_name);
+                            _PyType_Name(Py_TYPE(path)));
     }
 
     path_repr = _PyObject_CallNoArg(func);
@@ -13348,8 +13378,8 @@ PyOS_FSPath(PyObject *path)
     if (!(PyUnicode_Check(path_repr) || PyBytes_Check(path_repr))) {
         PyErr_Format(PyExc_TypeError,
                      "expected %.200s.__fspath__() to return str or bytes, "
-                     "not %.200s", Py_TYPE(path)->tp_name,
-                     Py_TYPE(path_repr)->tp_name);
+                     "not %.200s", _PyType_Name(Py_TYPE(path)),
+                     _PyType_Name(Py_TYPE(path_repr)));
         Py_DECREF(path_repr);
         return NULL;
     }
@@ -14258,12 +14288,12 @@ static struct PyModuleDef posixmodule = {
     PyModuleDef_HEAD_INIT,
     MODNAME,
     posix__doc__,
-    -1,
+    sizeof(_posixstate),
     posix_methods,
     NULL,
-    NULL,
-    NULL,
-    NULL
+    _posix_traverse,
+    _posix_clear,
+    _posix_free,
 };
 
 
@@ -14408,6 +14438,12 @@ INITFUNC(void)
     PyObject *list;
     const char * const *trace;
 
+    m = PyState_FindModule(&posixmodule);
+    if (m != NULL) {
+        Py_INCREF(m);
+        return m;
+    }
+
     m = PyModule_Create(&posixmodule);
     if (m == NULL)
         return NULL;
@@ -14429,94 +14465,106 @@ INITFUNC(void)
     PyModule_AddObject(m, "error", PyExc_OSError);
 
 #ifdef HAVE_PUTENV
-    if (posix_putenv_garbage == NULL)
-        posix_putenv_garbage = PyDict_New();
+    /* Save putenv() parameters as values here, so we can collect them when they
+     * get re-set with another call for the same key. */
+    _posixstate(m)->posix_putenv_garbage = PyDict_New();
 #endif
 
-    if (!initialized) {
 #if defined(HAVE_WAITID) && !defined(__APPLE__)
-        waitid_result_desc.name = MODNAME ".waitid_result";
-        WaitidResultType = PyStructSequence_NewType(&waitid_result_desc);
-        if (WaitidResultType == NULL) {
-            return NULL;
-        }
+    waitid_result_desc.name = MODNAME ".waitid_result";
+    PyObject *WaitidResultType = (PyObject *)PyStructSequence_NewType(&waitid_result_desc);
+    if (WaitidResultType == NULL) {
+        return NULL;
+    }
+    Py_INCREF(WaitidResultType);
+    PyModule_AddObject(m, "waitid_result", WaitidResultType);
+    _posixstate(m)->WaitidResultType = WaitidResultType;
 #endif
 
-        stat_result_desc.name = "os.stat_result"; /* see issue #19209 */
-        stat_result_desc.fields[7].name = PyStructSequence_UnnamedField;
-        stat_result_desc.fields[8].name = PyStructSequence_UnnamedField;
-        stat_result_desc.fields[9].name = PyStructSequence_UnnamedField;
-        StatResultType = PyStructSequence_NewType(&stat_result_desc);
-        if (StatResultType == NULL) {
-            return NULL;
-        }
-        structseq_new = StatResultType->tp_new;
-        StatResultType->tp_new = statresult_new;
+    stat_result_desc.name = "os.stat_result"; /* see issue #19209 */
+    stat_result_desc.fields[7].name = PyStructSequence_UnnamedField;
+    stat_result_desc.fields[8].name = PyStructSequence_UnnamedField;
+    stat_result_desc.fields[9].name = PyStructSequence_UnnamedField;
+    PyObject *StatResultType = (PyObject *)PyStructSequence_NewType(&stat_result_desc);
+    if (StatResultType == NULL) {
+        return NULL;
+    }
+    Py_INCREF(StatResultType);
+    PyModule_AddObject(m, "stat_result", StatResultType);
+    _posixstate(m)->StatResultType = StatResultType;
+    structseq_new = ((PyTypeObject *)StatResultType)->tp_new;
+    ((PyTypeObject *)StatResultType)->tp_new = statresult_new;
 
-        statvfs_result_desc.name = "os.statvfs_result"; /* see issue #19209 */
-        StatVFSResultType = PyStructSequence_NewType(&statvfs_result_desc);
-        if (StatVFSResultType == NULL) {
-            return NULL;
-        }
+    statvfs_result_desc.name = "os.statvfs_result"; /* see issue #19209 */
+    PyObject *StatVFSResultType = (PyObject *)PyStructSequence_NewType(&statvfs_result_desc);
+    if (StatVFSResultType == NULL) {
+        return NULL;
+    }
+    Py_INCREF(StatVFSResultType);
+    PyModule_AddObject(m, "statvfs_result", StatVFSResultType);
+    _posixstate(m)->StatVFSResultType = StatVFSResultType;
 #ifdef NEED_TICKS_PER_SECOND
 #  if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
-        ticks_per_second = sysconf(_SC_CLK_TCK);
+    ticks_per_second = sysconf(_SC_CLK_TCK);
 #  elif defined(HZ)
-        ticks_per_second = HZ;
+    ticks_per_second = HZ;
 #  else
-        ticks_per_second = 60; /* magic fallback value; may be bogus */
+    ticks_per_second = 60; /* magic fallback value; may be bogus */
 #  endif
 #endif
 
 #if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM)
-        sched_param_desc.name = MODNAME ".sched_param";
-        SchedParamType = PyStructSequence_NewType(&sched_param_desc);
-        if (SchedParamType == NULL) {
-            return NULL;
-        }
-        SchedParamType->tp_new = os_sched_param;
+    sched_param_desc.name = MODNAME ".sched_param";
+    PyObject *SchedParamType = (PyObject *)PyStructSequence_NewType(&sched_param_desc);
+    if (SchedParamType == NULL) {
+        return NULL;
+    }
+    Py_INCREF(SchedParamType);
+    PyModule_AddObject(m, "sched_param", SchedParamType);
+    _posixstate(m)->SchedParamType = SchedParamType;
+    ((PyTypeObject *)SchedParamType)->tp_new = os_sched_param;
 #endif
 
-        /* initialize TerminalSize_info */
-        TerminalSizeType = PyStructSequence_NewType(&TerminalSize_desc);
-        if (TerminalSizeType == NULL) {
-            return NULL;
-        }
+    /* initialize TerminalSize_info */
+    PyObject *TerminalSizeType = (PyObject *)PyStructSequence_NewType(&TerminalSize_desc);
+    if (TerminalSizeType == NULL) {
+        return NULL;
+    }
+    Py_INCREF(TerminalSizeType);
+    PyModule_AddObject(m, "terminal_size", TerminalSizeType);
+    _posixstate(m)->TerminalSizeType = TerminalSizeType;
 
-        /* initialize scandir types */
-        if (PyType_Ready(&ScandirIteratorType) < 0)
-            return NULL;
-        if (PyType_Ready(&DirEntryType) < 0)
-            return NULL;
+    /* initialize scandir types */
+    PyObject *ScandirIteratorType = PyType_FromSpec(&ScandirIteratorType_spec);
+    if (ScandirIteratorType == NULL) {
+        return NULL;
     }
-#if defined(HAVE_WAITID) && !defined(__APPLE__)
-    Py_INCREF((PyObject*) WaitidResultType);
-    PyModule_AddObject(m, "waitid_result", (PyObject*) WaitidResultType);
-#endif
-    Py_INCREF((PyObject*) StatResultType);
-    PyModule_AddObject(m, "stat_result", (PyObject*) StatResultType);
-    Py_INCREF((PyObject*) StatVFSResultType);
-    PyModule_AddObject(m, "statvfs_result",
-                       (PyObject*) StatVFSResultType);
+    _posixstate(m)->ScandirIteratorType = ScandirIteratorType;
 
-#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER)
-    Py_INCREF(SchedParamType);
-    PyModule_AddObject(m, "sched_param", (PyObject *)SchedParamType);
-#endif
+    PyObject *DirEntryType = PyType_FromSpec(&DirEntryType_spec);
+    if (DirEntryType == NULL) {
+        return NULL;
+    }
+    Py_INCREF(DirEntryType);
+    PyModule_AddObject(m, "DirEntry", DirEntryType);
+    _posixstate(m)->DirEntryType = DirEntryType;
 
     times_result_desc.name = MODNAME ".times_result";
-    TimesResultType = PyStructSequence_NewType(&times_result_desc);
+    PyObject *TimesResultType = (PyObject *)PyStructSequence_NewType(&times_result_desc);
     if (TimesResultType == NULL) {
         return NULL;
     }
-    PyModule_AddObject(m, "times_result", (PyObject *)TimesResultType);
+    Py_INCREF(TimesResultType);
+    PyModule_AddObject(m, "times_result", TimesResultType);
+    _posixstate(m)->TimesResultType = TimesResultType;
 
-    uname_result_desc.name = MODNAME ".uname_result";
-    UnameResultType = PyStructSequence_NewType(&uname_result_desc);
+    PyTypeObject *UnameResultType = PyStructSequence_NewType(&uname_result_desc);
     if (UnameResultType == NULL) {
         return NULL;
     }
+    Py_INCREF(UnameResultType);
     PyModule_AddObject(m, "uname_result", (PyObject *)UnameResultType);
+    _posixstate(m)->UnameResultType = (PyObject *)UnameResultType;
 
 #ifdef __APPLE__
     /*
@@ -14556,11 +14604,15 @@ INITFUNC(void)
 
 #endif /* __APPLE__ */
 
-    Py_INCREF(TerminalSizeType);
-    PyModule_AddObject(m, "terminal_size", (PyObject*)TerminalSizeType);
-
-    billion = PyLong_FromLong(1000000000);
-    if (!billion)
+    if ((_posixstate(m)->billion = PyLong_FromLong(1000000000)) == NULL)
+        return NULL;
+#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4)
+    _posixstate(m)->struct_rusage = PyUnicode_InternFromString("struct_rusage");
+    if (_posixstate(m)->struct_rusage == NULL)
+        return NULL;
+#endif
+    _posixstate(m)->st_mode = PyUnicode_InternFromString("st_mode");
+    if (_posixstate(m)->st_mode == NULL)
         return NULL;
 
     /* suppress "function not used" warnings */
@@ -14590,11 +14642,6 @@ INITFUNC(void)
     }
     PyModule_AddObject(m, "_have_functions", list);
 
-    Py_INCREF((PyObject *) &DirEntryType);
-    PyModule_AddObject(m, "DirEntry", (PyObject *)&DirEntryType);
-
-    initialized = 1;
-
     return m;
 }
 



More information about the Python-checkins mailing list