summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_capi.py31
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2021-09-08-00-30-09.bpo-44050.mFI15u.rst3
-rw-r--r--Modules/_testmultiphase.c22
-rw-r--r--Python/import.c4
4 files changed, 59 insertions, 1 deletions
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index db029ef..bdb8f76 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -766,6 +766,37 @@ class SubinterpreterTest(unittest.TestCase):
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 0000000..d6eed9f
--- /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 ad60f32..e0ed77d 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 317a836..d7f1267 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,