8000 Add _PyInterpreterConfig.check_multi_interp_extensions and Py_RTFLAGS… · python/cpython@49b4895 · GitHub
[go: up one dir, main page]

Skip to content

Commit 49b4895

Browse files
Add _PyInterpreterConfig.check_multi_interp_extensions and Py_RTFLAGS_MULTI_INTERP_EXTENSIONS.
1 parent eb9d241 commit 49b4895

File tree

8 files changed

+64
-42
lines changed

8 files changed

+64
-42
lines changed

Include/cpython/initconfig.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ typedef struct {
248248
int allow_exec;
249249
int allow_threads;
250250
int allow_daemon_threads;
251+
int check_multi_interp_extensions;
251252
} _PyInterpreterConfig;
252253

253254
#define _PyInterpreterConfig_INIT \
@@ -256,6 +257,7 @@ typedef struct {
256257
.allow_exec = 0, \
257258
.allow_threads = 1, \
258259
.allow_daemon_threads = 0, \
260+
.check_multi_interp_extensions = 1, \
259261
}
260262

261263
#define _PyInterpreterConfig_LEGACY_INIT \
@@ -264,6 +266,7 @@ typedef struct {
264266
.allow_exec = 1, \
265267
.allow_threads = 1, \
266268
.allow_daemon_threads = 1, \
269+
.check_multi_interp_extensions = 0, \
267270
}
268271

269272
/* --- Helper functions --------------------------------------- */

Include/cpython/pystate.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ is available in a given context. For example, forking the process
1111
might not be allowed in the current interpreter (i.e. os.fork() would fail).
1212
*/
1313

14+
/* Set if import should check a module for subinterpreter support. */
15+
#define Py_RTFLAGS_MULTI_INTERP_EXTENSIONS (1UL << 8)
16+
1417
/* Set if threads are allowed. */
1518
#define Py_RTFLAGS_THREADS (1UL << 10)
1619

Lib/test/test_capi/test_misc.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,17 +1297,20 @@ def test_configured_settings(self):
12971297
"""
12981298
import json
12991299

1300+
EXTENSIONS = 1<<8
13001301
THREADS = 1<<10
13011302
DAEMON_THREADS = 1<<11
13021303
FORK = 1<<15
13031304
EXEC = 1<<16
13041305

1305-
features = ['fork', 'exec', 'threads', 'daemon_threads']
1306+
features = ['fork', 'exec', 'threads', 'daemon_threads', 'extensions']
13061307
kwlist = [f'allow_{n}' for n in features]
1308+
kwlist[-1] = 'check_multi_interp_extensions'
13071309
for config, expected in {
1308-
(True, True, True, True): FORK | EXEC | THREADS | DAEMON_THREADS,
1309-
(False, False, False, False): 0,
1310-
(False, False, True, False): THREADS,
1310+
(True, True, True, True, True):
1311+
FORK | EXEC | THREADS | DAEMON_THREADS | EXTENSIONS,
1312+
(False, False, False, False, False): 0,
1313+
(False, False, True, False, True): THREADS | EXTENSIONS,
13111314
}.items():
13121315
kwargs = dict(zip(kwlist, config))
13131316
expected = {

Lib/test/test_embed.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1652,13 +1652,15 @@ def test_init_use_frozen_modules(self):
16521652
api=API_PYTHON, env=env)
16531653

16541654
def test_init_main_interpreter_settings(self):
1655+
EXTENSIONS = 1<<8
16551656
THREADS = 1<<10
16561657
DAEMON_THREADS = 1<<11
16571658
FORK = 1<<15
16581659
EXEC = 1<<16
16591660
expected = {
16601661
# All optional features should be enabled.
1661-
'feature_flags': FORK | EXEC | THREADS | DAEMON_THREADS,
1662+
'feature_flags':
1663+
FORK | EXEC | THREADS | DAEMON_THREADS,
16621664
}
16631665
out, err = self.run_embedded_interpreter(
16641666
'test_init_main_interpreter_settings',

Lib/test/test_import/__init__.py

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,13 +1430,17 @@ def import_script(self, name, fd):
14301430
os.write({fd}, text.encode('utf-8'))
14311431
''')
14321432

1433-
def check_compatible_shared(self, name):
F438 1433+
def check_compatible_shared(self, name, *, strict=False):
14341434
# Verify that the named module may be imported in a subinterpreter.
14351435
#
14361436
# The subinterpreter will be in the current process.
14371437
# The module will have already been imported in the main interpreter.
14381438
# Thus, for extension/builtin modules, the module definition will
14391439
# have been loaded already and cached globally.
1440+
#
1441+
# "strict" determines whether or not the interpreter will be
1442+
# configured to check for modules that are not compatible
1443+
# with use in multiple interpreters.
14401444

14411445
# This check should always pass for all modules if not strict.
14421446

@@ -1446,12 +1450,13 @@ def check_compatible_shared(self, name):
14461450
ret = run_in_subinterp_with_config(
14471451
self.import_script(name, w),
14481452
**self.RUN_KWARGS,
1453+
check_multi_interp_extensions=strict,
14491454
)
14501455
self.assertEqual(ret, 0)
14511456
out = os.read(r, 100)
14521457
self.assertEqual(out, b'okay')
14531458

1454-
def check_compatible_isolated(self, name):
1459+
def check_compatible_isolated(self, name, *, strict=False):
14551460
# Differences from check_compatible_shared():
14561461
# * subinterpreter in a new process
14571462
# * module has never been imported before in that process
@@ -1465,67 +1470,60 @@ def check_compatible_isolated(self, name):
14651470
ret = _testcapi.run_in_subinterp_with_config(
14661471
{self.import_script(name, "sys.stdout.fileno()")!r},
14671472
**{self.RUN_KWARGS},
1473+
check_multi_interp_extensions={strict},
14681474
)
14691475
assert ret == 0, ret
14701476
'''))
14711477
self.assertEqual(err, b'')
14721478
self.assertEqual(out, b'okay')
14731479

1474-
def check_incompatible_isolated(self, name):
1475-
# Differences from check_compatible_isolated():
1476-
# * verify that import fails
1477-
_, out, err = script_helper.assert_python_ok('-c', textwrap.dedent(f'''
1478-
import _testcapi, sys
1479-
assert {name!r} not in sys.modules, {name!r}
1480-
ret = _testcapi.run_in_subinterp_with_config(
1481-
{self.import_script(name, "sys.stdout.fileno()")!r},
1482-
**{self.RUN_KWARGS},
1483-
)
1484-
assert ret == 0, ret
1485-
'''))
1486-
self.assertEqual(err, b'')
1487-
self.assertEqual(
1488-
out.decode('utf-8'),
1489-
f'ImportError: module {name} does not support loading in subinterpreters',
1490-
)
1491-
14921480
def test_builtin_compat(self):
14931481
module = 'sys'
1494-
with self.subTest(f'{module}: shared'):
1495-
self.check_compatible_shared(module)
1482+
with self.subTest(f'{module}: not strict'):
1483+
self.check_compatible_shared(module, strict=False)
1484+
with self.subTest(f'{module}: strict, shared'):
1485+
self.check_compatible_shared(module, strict=True)
14961486

14971487
@cpython_only
14981488
def test_frozen_compat(self):
14991489
module = '_frozen_importlib'
15001490
if __import__(module).__spec__.origin != 'frozen':
15011491
raise unittest.SkipTest(f'{module} is unexpectedly not frozen')
1502-
with self.subTest(f'{module}: shared'):
1503-
self.check_compatible_shared(module)
1492+
with self.subTest(f'{module}: not strict'):
1493+
self.check_compatible_shared(module, strict=False)
1494+
with self.subTest(f'{module}: strict, shared'):
1495+
self.check_compatible_shared(module, strict=True)
15041496

1505-
@unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module")
1497+
@unittest.skipIf(_testsinglephase is None, "test requires _testsinglphase module")
15061498
def test_single_init_extension_compat(self):
15071499
module = '_testsinglephase'
1508-
with self.subTest(f'{module}: shared'):
1500+
with self.subTest(f'{module}: not strict'):
1501+
self.check_compatible_shared(module, strict=False)
1502+
with self.subTest(f'{module}: strict, shared'):
15091503
self.check_compatible_shared(module)
1510-
with self.subTest(f'{module}: isolated'):
1504+
with self.subTest(f'{module}: strict, isolated'):
15111505
self.check_compatible_isolated(module)
15121506

15131507
@unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module")
15141508
def test_multi_init_extension_compat(self):
15151509
module = '_testmultiphase'
1516-
with self.subTest(f'{module}: shared'):
1517-
self.check_compatible_shared(module)
1518-
with self.subTest(f'{module}: isolated'):
1519-
self.check_compatible_isolated(module)
1510+
with self.subTest(f'{module}: not strict'):
1511+
self.check_compatible_shared(module, strict=False)
1512+
with self.subTest(f'{module}: strict, shared'):
1513+
self.check_compatible_shared(module, strict=True)
1514+
with self.subTest(f'{module}: strict, isolated'):
1515+
self.check_compatible_isolated(module, strict=True)
15201516

15211517
def test_python_compat(self):
15221518
module = 'threading'
15231519
if __import__(module).__spec__.origin == 'frozen':
15241520
raise unittest.SkipTest(f'{module} is unexpectedly frozen')
1525-
with self.subTest(f'{module}: shared'):
1526-
self.check_compatible_shared(module)
1527-
with self.subTest(f'{module}: isolated'):
1528-
self.check_compatible_isolated(module)
1521+
with self.subTest(f'{module}: not strict'):
1522+
self.check_compatible_shared(module, strict=False)
1523+
with self.subTest(f'{module}: strict, shared'):
1524+
self.check_compatible_shared(module, strict=True)
1525+
with self.subTest(f'{module}: strict, isolated'):
1526+
self.check_compatible_isolated(module, strict=True)
15291527

15301528

15311529
if __name__ == '__main__':

Lib/test/test_threading.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,7 @@ def func():
13471347
allow_exec=True,
13481348
allow_threads={allowed},
13491349
allow_daemon_threads={daemon_allowed},
1350+
check_multi_interp_extensions=False,
13501351
)
13511352
""")
13521353
with test.support.SuppressCrashReport():

Modules/_testcapimodule.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,7 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
15781578
int allow_exec = -1;
15791579
int allow_threads = -1;
15801580
int allow_daemon_threads = -1;
1581+
int check_multi_interp_extensions = -1;
15811582
int r;
15821583
PyThreadState *substate, *mainstate;
15831584
/* only initialise 'cflags.cf_flags' to test backwards compatibility */
@@ -1588,11 +1589,13 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
15881589
"allow_exec",
15891590
"allow_threads",
15901591
"allow_daemon_threads",
1592+
"check_multi_interp_extensions",
15911593
NULL};
15921594
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
1593-
"s$pppp:run_in_subinterp_with_config", kwlist,
1595+
"s$ppppp:run_in_subinterp_with_config", kwlist,
15941596
&code, &allow_fork, &allow_exec,
1595-
&allow_threads, &allow_daemon_threads)) {
1597+
&allow_threads, &allow_daemon_threads,
1598+
&check_multi_interp_extensions)) {
15961599
return NULL;
15971600
}
15981601
if (allow_fork < 0) {
@@ -1611,6 +1614,10 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
16111614
PyErr_SetString(PyExc_ValueError, "missing allow_daemon_threads");
16121615
return NULL;
16131616
}
1617+
if (check_multi_interp_extensions < 0) {
1618+
PyErr_SetString(PyExc_ValueError, "missing check_multi_interp_extensions");
1619+
return NULL;
1620+
}
16141621

16151622
mainstate = PyThreadState_Get();
16161623

@@ -1621,6 +1628,7 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
16211628
.allow_exec = allow_exec,
16221629
.allow_threads = allow_threads,
16231630
.allow_daemon_threads = allow_daemon_threads,
1631+
.check_multi_interp_extensions = check_multi_interp_extensions,
16241632
};
16251633
substate = _Py_NewInterpreterFromConfig(&config);
16261634
if (substate == NULL) {

Python/pylifecycle.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,10 @@ init_interp_settings(PyInterpreterState *interp, const _PyInterpreterConfig *con
642642
if (config->allow_daemon_threads) {
643643
interp->feature_flags |= Py_RTFLAGS_DAEMON_THREADS;
644644
}
645+
646+
if (config->check_multi_interp_extensions) {
647+
interp->feature_flags |= Py_RTFLAGS_MULTI_INTERP_EXTENSIONS;
648+
}
645649
}
646650

647651

0 commit comments

Comments
 (0)
0