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):
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 ***/
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,