diff options
author | Ćukasz Langa <lukasz@langa.pl> | 2021-10-05 20:30:25 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-05 20:30:25 (GMT) |
commit | 52d9d3b75441ae6038fadead89eac5eecdd34501 (patch) | |
tree | 8d779959a1d0da0eb29bbfe7046d2a643e1ca93b | |
parent | 950b324973a98ec45c21e7e7149415259a045ff7 (diff) | |
download | cpython-52d9d3b75441ae6038fadead89eac5eecdd34501.zip cpython-52d9d3b75441ae6038fadead89eac5eecdd34501.tar.gz cpython-52d9d3b75441ae6038fadead89eac5eecdd34501.tar.bz2 |
[3.9] bpo-44050: Extension modules can share state when they don't support sub-interpreters. (GH-27794) (GH-28741)
(cherry picked from commit b9bb74871b27d9226df2dd3fce9d42bda8b43c2b)
Co-authored-by: Hai Shi <shihai1992@gmail.com>
-rw-r--r-- | Lib/test/test_capi.py | 31 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2021-09-08-00-30-09.bpo-44050.mFI15u.rst | 3 | ||||
-rw-r--r-- | Modules/_testmultiphase.c | 24 | ||||
-rw-r--r-- | Python/import.c | 4 |
4 files changed, 61 insertions, 1 deletions
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 0cbe839..6b2fe2f 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -680,6 +680,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 d69ae62..4a4dbe7 100644 --- a/Modules/_testmultiphase.c +++ b/Modules/_testmultiphase.c @@ -834,6 +834,30 @@ 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; + } + + Py_INCREF(PyExc_Exception); + if (PyModule_AddObject(module, "Error", PyExc_Exception) < 0) { + Py_DECREF(PyExc_Exception); + Py_DECREF(module); + return NULL; + } + return module; +} + /*** Helper for imp test ***/ diff --git a/Python/import.c b/Python/import.c index 1ec7553..8358d70 100644 --- a/Python/import.c +++ b/Python/import.c @@ -710,7 +710,9 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, return -1; } - if (_Py_IsMainInterpreter(tstate)) { + // 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) || def->m_size == -1) { if (def->m_size == -1) { if (def->m_base.m_copy) { /* Somebody already imported the module, |