Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit efc84cd

Browse filesBrowse files
ericsnowcurrentlyjbower-fb
authored andcommitted
pythongh-104108: Add the Py_mod_multiple_interpreters Module Def Slot (pythongh-104148)
I'll be adding a value to indicate support for per-interpreter GIL in pythongh-99114.
1 parent 0826f80 commit efc84cd
Copy full SHA for efc84cd

File tree

5 files changed

+122
-22
lines changed
Filter options

5 files changed

+122
-22
lines changed

‎Include/moduleobject.h

Copy file name to clipboardExpand all lines: Include/moduleobject.h
+6-1Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,16 @@ struct PyModuleDef_Slot {
7878

7979
#define Py_mod_create 1
8080
#define Py_mod_exec 2
81+
#define Py_mod_multiple_interpreters 3
8182

8283
#ifndef Py_LIMITED_API
83-
#define _Py_mod_LAST_SLOT 2
84+
#define _Py_mod_LAST_SLOT 3
8485
#endif
8586

87+
/* for Py_mod_multiple_interpreters: */
88+
#define Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED ((void *)0)
89+
#define Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED ((void *)1)
90+
8691
#endif /* New in 3.5 */
8792

8893
struct PyModuleDef {

‎Lib/test/test_import/__init__.py

Copy file name to clipboardExpand all lines: Lib/test/test_import/__init__.py
+61-21Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,26 +1652,44 @@ def pipe(self):
16521652
os.set_blocking(r, False)
16531653
return (r, w)
16541654

1655-
def import_script(self, name, fd, check_override=None):
1655+
def import_script(self, name, fd, filename=None, check_override=None):
16561656
override_text = ''
16571657
if check_override is not None:
16581658
override_text = f'''
1659-
import _imp
1660-
_imp._override_multi_interp_extensions_check({check_override})
1661-
'''
1662-
return textwrap.dedent(f'''
1663-
import os, sys
1664-
{override_text}
1665-
try:
1666-
import {name}
1667-
except ImportError as exc:
1668-
text = 'ImportError: ' + str(exc)
1669-
else:
1670-
text = 'okay'
1671-
os.write({fd}, text.encode('utf-8'))
1672-
''')
1659+
import _imp
1660+
_imp._override_multi_interp_extensions_check({check_override})
1661+
'''
1662+
if filename:
1663+
return textwrap.dedent(f'''
1664+
from importlib.util import spec_from_loader, module_from_spec
1665+
from importlib.machinery import ExtensionFileLoader
1666+
import os, sys
1667+
{override_text}
1668+
loader = ExtensionFileLoader({name!r}, {filename!r})
1669+
spec = spec_from_loader({name!r}, loader)
1670+
try:
1671+
module = module_from_spec(spec)
1672+
loader.exec_module(module)
1673+
except ImportError as exc:
1674+
text = 'ImportError: ' + str(exc)
1675+
else:
1676+
text = 'okay'
1677+
os.write({fd}, text.encode('utf-8'))
1678+
''')
1679+
else:
1680+
return textwrap.dedent(f'''
1681+
import os, sys
1682+
{override_text}
1683+
try:
1684+
import {name}
1685+
except ImportError as exc:
1686+
text = 'ImportError: ' + str(exc)
1687+
else:
1688+
text = 'okay'
1689+
os.write({fd}, text.encode('utf-8'))
1690+
''')
16731691

1674-
def run_here(self, name, *,
1692+
def run_here(self, name, filename=None, *,
16751693
check_singlephase_setting=False,
16761694
check_singlephase_override=None,
16771695
isolated=False,
@@ -1700,26 +1718,30 @@ def run_here(self, name, *,
17001718
)
17011719

17021720
r, w = self.pipe()
1703-
script = self.import_script(name, w, check_singlephase_override)
1721+
script = self.import_script(name, w, filename,
1722+
check_singlephase_override)
17041723

17051724
ret = run_in_subinterp_with_config(script, **kwargs)
17061725
self.assertEqual(ret, 0)
17071726
return os.read(r, 100)
17081727

1709-
def check_compatible_here(self, name, *, strict=False, isolated=False):
1728+
def check_compatible_here(self, name, filename=None, *,
1729+
strict=False,
1730+
isolated=False,
1731+
):
17101732
# Verify that the named module may be imported in a subinterpreter.
17111733
# (See run_here() for more info.)
1712-
out = self.run_here(name,
1734+
out = self.run_here(name, filename,
17131735
check_singlephase_setting=strict,
17141736
isolated=isolated,
17151737
)
17161738
self.assertEqual(out, b'okay')
17171739

1718-
def check_incompatible_here(self, name, *, isolated=False):
1740+
def check_incompatible_here(self, name, filename=None, *, isolated=False):
17191741
# Differences from check_compatible_here():
17201742
# * verify that import fails
17211743
# * "strict" is always True
1722-
out = self.run_here(name,
1744+
out = self.run_here(name, filename,
17231745
check_singlephase_setting=True,
17241746
isolated=isolated,
17251747
)
@@ -1820,6 +1842,24 @@ def test_multi_init_extension_compat(self):
18201842
with self.subTest(f'{module}: strict, fresh'):
18211843
self.check_compatible_fresh(module, strict=True)
18221844

1845+
@unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module")
1846+
def test_multi_init_extension_non_isolated_compat(self):
1847+
modname = '_test_non_isolated'
1848+
filename = _testmultiphase.__file__
1849+
loader = ExtensionFileLoader(modname, filename)
1850+
spec = importlib.util.spec_from_loader(modname, loader)
1851+
module = importlib.util.module_from_spec(spec)
1852+
loader.exec_module(module)
1853+
sys.modules[modname] = module
1854+
1855+
require_extension(module)
1856+
with self.subTest(f'{modname}: isolated'):
1857+
self.check_incompatible_here(modname, filename, isolated=True)
1858+
with self.subTest(f'{modname}: not isolated'):
1859+
self.check_incompatible_here(modname, filename, isolated=False)
1860+
with self.subTest(f'{modname}: not strict'):
1861+
self.check_compatible_here(modname, filename, strict=False)
1862+
18231863
def test_python_compat(self):
18241864
module = 'threading'
18251865
require_pure_python(module)
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Multi-phase init extension modules may now indicate whether or not they
2+
actually support multiple interpreters. By default such modules are
3+
expected to support use in multiple interpreters. In the uncommon case that
4+
one does not, it may use the new ``Py_mod_multiple_interpreters`` module def
5+
slot. A value of ``0`` means the module does not support them. ``1`` means
6+
it does. The default is ``1``.

‎Modules/_testmultiphase.c

Copy file name to clipboardExpand all lines: Modules/_testmultiphase.c
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,3 +884,22 @@ PyInit__test_module_state_shared(void)
884884
}
885885
return module;
886886
}
887+
888+
889+
/* multiple interpreters supports */
890+
891+
static PyModuleDef_Slot non_isolated_slots[] = {
892+
{Py_mod_exec, execfunc},
893+
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
894+
{0, NULL},
895+
};
896+
897+
static PyModuleDef non_isolated_def = TEST_MODULE_DEF("_test_non_isolated",
898+
non_isolated_slots,
899+
testexport_methods);
900+
901+
PyMODINIT_FUNC
902+
PyInit__test_non_isolated(void)
903+
{
904+
return PyModuleDef_Init(&non_isolated_def);
905+
}

‎Objects/moduleobject.c

Copy file name to clipboardExpand all lines: Objects/moduleobject.c
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio
245245
PyObject *(*create)(PyObject *, PyModuleDef*) = NULL;
246246
PyObject *nameobj;
247247
PyObject *m = NULL;
248+
int has_multiple_interpreters_slot = 0;
249+
void *multiple_interpreters = (void *)0;
248250
int has_execution_slots = 0;
249251
const char *name;
250252
int ret;
@@ -287,6 +289,17 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio
287289
case Py_mod_exec:
288290
has_execution_slots = 1;
289291
break;
292+
case Py_mod_multiple_interpreters:
293+
if (has_multiple_interpreters_slot) {
294+
PyErr_Format(
295+
PyExc_SystemError,
296+
"module %s has more than one 'multiple interpreters' slots",
297+
name);
298+
goto error;
299+
}
300+
multiple_interpreters = cur_slot->value;
301+
has_multiple_interpreters_slot = 1;
302+
break;
290303
default:
291304
assert(cur_slot->slot < 0 || cur_slot->slot > _Py_mod_LAST_SLOT);
292305
PyErr_Format(
@@ -297,6 +310,20 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio
297310
}
298311
}
299312

313+
/* By default, multi-phase init modules are expected
314+
to work under multiple interpreters. */
315+
if (!has_multiple_interpreters_slot) {
316+
multiple_interpreters = Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED;
317+
}
318+
if (multiple_interpreters == Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED) {
319+
PyInterpreterState *interp = _PyInterpreterState_GET();
320+
if (!_Py_IsMainInterpreter(interp)
321+
&& _PyImport_CheckSubinterpIncompatibleExtensionAllowed(name) < 0)
322+
{
323+
goto error;
324+
}
325+
}
326+
300327
if (create) {
301328
m = create(spec, def);
302329
if (m == NULL) {
@@ -421,6 +448,9 @@ PyModule_ExecDef(PyObject *module, PyModuleDef *def)
421448
return -1;
422449
}
423450
break;
451+
case Py_mod_multiple_interpreters:
452+
/* handled in PyModule_FromDefAndSpec2 */
453+
break;
424454
default:
425455
PyErr_Format(
426456
PyExc_SystemError,

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.