Skip to content

Commit 52d9d3b

Browse files
ambvshihai1991
andauthored
[3.9] bpo-44050: Extension modules can share state when they don't support sub-interpreters. (GH-27794) (GH-28741)
(cherry picked from commit b9bb748) Co-authored-by: Hai Shi <shihai1992@gmail.com>
1 parent 950b324 commit 52d9d3b

File tree

4 files changed

+61
-1
lines changed

4 files changed

+61
-1
lines changed

Lib/test/test_capi.py

+31
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,37 @@ def test_mutate_exception(self):
680680

681681
self.assertFalse(hasattr(binascii.Error, "foobar"))
682682

683+
def test_module_state_shared_in_global(self):
684+
"""
685+
bpo-44050: Extension module state should be shared between interpreters
686+
when it doesn't support sub-interpreters.
687+
"""
688+
r, w = os.pipe()
689+
self.addCleanup(os.close, r)
690+
self.addCleanup(os.close, w)
691+
692+
script = textwrap.dedent(f"""
693+
import importlib.machinery
694+
import importlib.util
695+
import os
696+
697+
fullname = '_test_module_state_shared'
698+
origin = importlib.util.find_spec('_testmultiphase').origin
699+
loader = importlib.machinery.ExtensionFileLoader(fullname, origin)
700+
spec = importlib.util.spec_from_loader(fullname, loader)
701+
module = importlib.util.module_from_spec(spec)
702+
attr_id = str(id(module.Error)).encode()
703+
704+
os.write({w}, attr_id)
705+
""")
706+
exec(script)
707+
main_attr_id = os.read(r, 100)
708+
709+
ret = support.run_in_subinterp(script)
710+
self.assertEqual(ret, 0)
711+
subinterp_attr_id = os.read(r, 100)
712+
self.assertEqual(main_attr_id, subinterp_attr_id)
713+
683714

684715
class TestThreadState(unittest.TestCase):
685716

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Extensions that indicate they use global state (by setting ``m_size`` to -1)
2+
can again be used in multiple interpreters. This reverts to behavior of
3+
Python 3.8.

Modules/_testmultiphase.c

+24
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,30 @@ PyInit__testmultiphase_meth_state_access(PyObject *spec)
834834
return PyModuleDef_Init(&def_meth_state_access);
835835
}
836836

837+
static PyModuleDef def_module_state_shared = {
838+
PyModuleDef_HEAD_INIT,
839+
.m_name = "_test_module_state_shared",
840+
.m_doc = PyDoc_STR("Regression Test module for single-phase init."),
841+
.m_size = -1,
842+
};
843+
844+
PyMODINIT_FUNC
845+
PyInit__test_module_state_shared(PyObject *spec)
846+
{
847+
PyObject *module = PyModule_Create(&def_module_state_shared);
848+
if (module == NULL) {
849+
return NULL;
850+
}
851+
852+
Py_INCREF(PyExc_Exception);
853+
if (PyModule_AddObject(module, "Error", PyExc_Exception) < 0) {
854+
Py_DECREF(PyExc_Exception);
855+
Py_DECREF(module);
856+
return NULL;
857+
}
858+
return module;
859+
}
860+
837861

838862
/*** Helper for imp test ***/
839863

Python/import.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,9 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name,
710710
return -1;
711711
}
712712

713-
if (_Py_IsMainInterpreter(tstate)) {
713+
// bpo-44050: Extensions and def->m_base.m_copy can be updated
714+
// when the extension module doesn't support sub-interpreters.
715+
if (_Py_IsMainInterpreter(tstate) || def->m_size == -1) {
714716
if (def->m_size == -1) {
715717
if (def->m_base.m_copy) {
716718
/* Somebody already imported the module,

0 commit comments

Comments
 (0)