[3.9] bpo-44050: Extension modules can share state when they don't su… · python/cpython@52d9d3b

4 files changed

lines changed

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.

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
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,