Skip to content

Commit b9bb748

Browse files
authored
bpo-44050: Extension modules can share state when they don't support sub-interpreters. (GH-27794)
Automerge-Triggered-By: GH:encukou
1 parent 5146877 commit b9bb748

File tree

4 files changed

+59
-1
lines changed

4 files changed

+59
-1
lines changed

Lib/test/test_capi.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,37 @@ def test_mutate_exception(self):
766766

767767
self.assertFalse(hasattr(binascii.Error, "foobar"))
768768

769+
def test_module_state_shared_in_global(self):
770+
"""
771+
bpo-44050: Extension module state should be shared between interpreters
772+
when it doesn't support sub-interpreters.
773+
"""
774+
r, w = os.pipe()
775+
self.addCleanup(os.close, r)
776+
self.addCleanup(os.close, w)
777+
778+
script = textwrap.dedent(f"""
779+
import importlib.machinery
780+
import importlib.util
781+
import os
782+
783+
fullname = '_test_module_state_shared'
784+
origin = importlib.util.find_spec('_testmultiphase').origin
785+
loader = importlib.machinery.ExtensionFileLoader(fullname, origin)
786+
spec = importlib.util.spec_from_loader(fullname, loader)
787+
module = importlib.util.module_from_spec(spec)
788+
attr_id = str(id(module.Error)).encode()
789+
790+
os.write({w}, attr_id)
791+
""")
792+
exec(script)
793+
main_attr_id = os.read(r, 100)
794+
795+
ret = support.run_in_subinterp(script)
796+
self.assertEqual(ret, 0)
797+
subinterp_attr_id = os.read(r, 100)
798+
self.assertEqual(main_attr_id, subinterp_attr_id)
799+
769800

770801
class TestThreadState(unittest.TestCase):
771802

Lines changed: 3 additions & 0 deletions
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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,28 @@ PyInit__testmultiphase_meth_state_access(PyObject *spec)
844844
return PyModuleDef_Init(&def_meth_state_access);
845845
}
846846

847+
static PyModuleDef def_module_state_shared = {
848+
PyModuleDef_HEAD_INIT,
849+
.m_name = "_test_module_state_shared",
850+
.m_doc = PyDoc_STR("Regression Test module for single-phase init."),
851+
.m_size = -1,
852+
};
853+
854+
PyMODINIT_FUNC
855+
PyInit__test_module_state_shared(PyObject *spec)
856+
{
857+
PyObject *module = PyModule_Create(&def_module_state_shared);
858+
if (module == NULL) {
859+
return NULL;
860+
}
861+
862+
if (PyModule_AddObjectRef(module, "Error", PyExc_Exception) < 0) {
863+
Py_DECREF(module);
864+
return NULL;
865+
}
866+
return module;
867+
}
868+
847869

848870
/*** Helper for imp test ***/
849871

Python/import.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,9 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name,
442442
return -1;
443443
}
444444

445-
if (_Py_IsMainInterpreter(tstate->interp)) {
445+
// bpo-44050: Extensions and def->m_base.m_copy can be updated
446+
// when the extension module doesn't support sub-interpreters.
447+
if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) {
446448
if (def->m_size == -1) {
447449
if (def->m_base.m_copy) {
448450
/* Somebody already imported the module,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy