[Python-checkins] bpo-44050: Extension modules can share state when they don't support sub-interpreters. (GH-27794)

miss-islington webhook-mailer at python.org
Tue Oct 5 09:19:40 EDT 2021


https://github.com/python/cpython/commit/b9bb74871b27d9226df2dd3fce9d42bda8b43c2b
commit: b9bb74871b27d9226df2dd3fce9d42bda8b43c2b
branch: main
author: Hai Shi <shihai1992 at gmail.com>
committer: miss-islington <31488909+miss-islington at users.noreply.github.com>
date: 2021-10-05T06:19:32-07:00
summary:

bpo-44050: Extension modules can share state when they don't support sub-interpreters. (GH-27794)



Automerge-Triggered-By: GH:encukou

files:
A Misc/NEWS.d/next/Core and Builtins/2021-09-08-00-30-09.bpo-44050.mFI15u.rst
M Lib/test/test_capi.py
M Modules/_testmultiphase.c
M Python/import.c

diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index db029ef731c38..bdb8f768fc313 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -766,6 +766,37 @@ def test_mutate_exception(self):
 
         self.assertFalse(hasattr(binascii.Error, "foobar"))
 
+    def test_module_state_shared_in_global(self):
+        """
+        bpo-44050: Extension module state should be shared between interpreters
+        when it doesn't support sub-interpreters.
+        """
+        r, w = os.pipe()
+        self.addCleanup(os.close, r)
+        self.addCleanup(os.close, w)
+
+        script = textwrap.dedent(f"""
+            import importlib.machinery
+            import importlib.util
+            import os
+
+            fullname = '_test_module_state_shared'
+            origin = importlib.util.find_spec('_testmultiphase').origin
+            loader = importlib.machinery.ExtensionFileLoader(fullname, origin)
+            spec = importlib.util.spec_from_loader(fullname, loader)
+            module = importlib.util.module_from_spec(spec)
+            attr_id = str(id(module.Error)).encode()
+
+            os.write({w}, attr_id)
+            """)
+        exec(script)
+        main_attr_id = os.read(r, 100)
+
+        ret = support.run_in_subinterp(script)
+        self.assertEqual(ret, 0)
+        subinterp_attr_id = os.read(r, 100)
+        self.assertEqual(main_attr_id, subinterp_attr_id)
+
 
 class TestThreadState(unittest.TestCase):
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-09-08-00-30-09.bpo-44050.mFI15u.rst b/Misc/NEWS.d/next/Core and Builtins/2021-09-08-00-30-09.bpo-44050.mFI15u.rst
new file mode 100644
index 0000000000000..d6eed9f1bcfe9
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2021-09-08-00-30-09.bpo-44050.mFI15u.rst	
@@ -0,0 +1,3 @@
+Extensions that indicate they use global state (by setting ``m_size`` to -1)
+can again be used in multiple interpreters. This reverts to behavior of
+Python 3.8.
diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c
index ad60f32f7e7a6..e0ed77d265cdc 100644
--- a/Modules/_testmultiphase.c
+++ b/Modules/_testmultiphase.c
@@ -844,6 +844,28 @@ PyInit__testmultiphase_meth_state_access(PyObject *spec)
     return PyModuleDef_Init(&def_meth_state_access);
 }
 
+static PyModuleDef def_module_state_shared = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "_test_module_state_shared",
+    .m_doc = PyDoc_STR("Regression Test module for single-phase init."),
+    .m_size = -1,
+};
+
+PyMODINIT_FUNC
+PyInit__test_module_state_shared(PyObject *spec)
+{
+    PyObject *module = PyModule_Create(&def_module_state_shared);
+    if (module == NULL) {
+        return NULL;
+    }
+
+    if (PyModule_AddObjectRef(module, "Error", PyExc_Exception) < 0) {
+        Py_DECREF(module);
+        return NULL;
+    }
+    return module;
+}
+
 
 /*** Helper for imp test ***/
 
diff --git a/Python/import.c b/Python/import.c
index 317a836617c51..d7f126784192b 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -442,7 +442,9 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name,
         return -1;
     }
 
-    if (_Py_IsMainInterpreter(tstate->interp)) {
+    // bpo-44050: Extensions and def->m_base.m_copy can be updated
+    // when the extension module doesn't support sub-interpreters.
+    if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) {
         if (def->m_size == -1) {
             if (def->m_base.m_copy) {
                 /* Somebody already imported the module,



More information about the Python-checkins mailing list