Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 252346a

Browse files
authored
bpo-40453: Add PyConfig._isolated_subinterpreter (GH-19820)
An isolated subinterpreter cannot spawn threads, spawn a child process or call os.fork(). * Add private _Py_NewInterpreter(isolated_subinterpreter) function. * Add isolated=True keyword-only parameter to _xxsubinterpreters.create(). * Allow again os.fork() in "non-isolated" subinterpreters.
1 parent 8bcfd31 commit 252346a

14 files changed

Lines changed: 68 additions & 12 deletions

Doc/c-api/init_config.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,8 @@ Private provisional API:
10041004
10051005
* :c:member:`PyConfig._init_main`: if set to 0,
10061006
:c:func:`Py_InitializeFromConfig` stops at the "Core" initialization phase.
1007+
* :c:member:`PyConfig._isolated_interpreter`: if non-zero,
1008+
disallow threads, subprocesses and fork.
10071009
10081010
.. c:function:: PyStatus _Py_InitializeMain(void)
10091011

Include/cpython/initconfig.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,10 @@ typedef struct {
409409

410410
/* If equal to 0, stop Python initialization before the "main" phase */
411411
int _init_main;
412+
413+
/* If non-zero, disallow threads, subprocesses, and fork.
414+
Default: 0. */
415+
int _isolated_interpreter;
412416
} PyConfig;
413417

414418
PyAPI_FUNC(void) PyConfig_InitPythonConfig(PyConfig *config);

Include/cpython/pylifecycle.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ PyAPI_FUNC(int) _Py_CoerceLegacyLocale(int warn);
6565
PyAPI_FUNC(int) _Py_LegacyLocaleDetected(int warn);
6666
PyAPI_FUNC(char *) _Py_SetLocaleFromEnv(int category);
6767

68+
PyAPI_FUNC(PyThreadState *) _Py_NewInterpreter(int isolated_subinterpreter);
69+
6870
#ifdef __cplusplus
6971
}
7072
#endif

Lib/test/test__xxsubinterpreters.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,7 @@ def f():
794794
self.assertEqual(out, 'it worked!')
795795

796796
def test_create_thread(self):
797+
subinterp = interpreters.create(isolated=False)
797798
script, file = _captured_script("""
798799
import threading
799800
def f():
@@ -804,7 +805,7 @@ def f():
804805
t.join()
805806
""")
806807
with file:
807-
interpreters.run_string(self.id, script)
808+
interpreters.run_string(subinterp, script)
808809
out = file.read()
809810

810811
self.assertEqual(out, 'it worked!')

Lib/test/test_embed.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
406406
'check_hash_pycs_mode': 'default',
407407
'pathconfig_warnings': 1,
408408
'_init_main': 1,
409+
'_isolated_interpreter': 0,
409410
}
410411
if MS_WINDOWS:
411412
CONFIG_COMPAT.update({
@@ -766,6 +767,8 @@ def test_init_from_config(self):
766767

767768
'check_hash_pycs_mode': 'always',
768769
'pathconfig_warnings': 0,
770+
771+
'_isolated_interpreter': 1,
769772
}
770773
self.check_all_configs("test_init_from_config", config, preconfig,
771774
api=API_COMPAT)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add ``isolated=True`` keyword-only parameter to
2+
``_xxsubinterpreters.create()``. An isolated subinterpreter cannot spawn
3+
threads, spawn a child process or call ``os.fork()``.

Modules/_posixsubprocess.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,14 @@ subprocess_fork_exec(PyObject* self, PyObject *args)
663663
return NULL;
664664
}
665665

666+
PyInterpreterState *interp = PyInterpreterState_Get();
667+
const PyConfig *config = _PyInterpreterState_GetConfig(interp);
668+
if (config->_isolated_interpreter) {
669+
PyErr_SetString(PyExc_RuntimeError,
670+
"subprocess not supported for isolated subinterpreters");
671+
return NULL;
672+
}
673+
666674
/* We need to call gc.disable() when we'll be calling preexec_fn */
667675
if (preexec_fn != Py_None) {
668676
PyObject *result;

Modules/_threadmodule.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,14 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
10851085
"optional 3rd arg must be a dictionary");
10861086
return NULL;
10871087
}
1088+
1089+
PyInterpreterState *interp = _PyInterpreterState_GET();
1090+
if (interp->config._isolated_interpreter) {
1091+
PyErr_SetString(PyExc_RuntimeError,
1092+
"thread is not supported for isolated subinterpreters");
1093+
return NULL;
1094+
}
1095+
10881096
boot = PyMem_NEW(struct bootstate, 1);
10891097
if (boot == NULL)
10901098
return PyErr_NoMemory();

Modules/_winapi.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,14 @@ _winapi_CreateProcess_impl(PyObject *module,
10801080
return NULL;
10811081
}
10821082

1083+
PyInterpreterState *interp = PyInterpreterState_Get();
1084+
const PyConfig *config = _PyInterpreterState_GetConfig(interp);
1085+
if (config->_isolated_interpreter) {
1086+
PyErr_SetString(PyExc_RuntimeError,
1087+
"subprocess not supported for isolated subinterpreters");
1088+
return NULL;
1089+
}
1090+
10831091
ZeroMemory(&si, sizeof(si));
10841092
si.StartupInfo.cb = sizeof(si);
10851093

Modules/_xxsubinterpretersmodule.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1999,16 +1999,20 @@ _global_channels(void) {
19991999
}
20002000

20012001
static PyObject *
2002-
interp_create(PyObject *self, PyObject *args)
2002+
interp_create(PyObject *self, PyObject *args, PyObject *kwds)
20032003
{
2004-
if (!PyArg_UnpackTuple(args, "create", 0, 0)) {
2004+
2005+
static char *kwlist[] = {"isolated", NULL};
2006+
int isolated = 1;
2007+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|$i:create", kwlist,
2008+
&isolated)) {
20052009
return NULL;
20062010
}
20072011

20082012
// Create and initialize the new interpreter.
20092013
PyThreadState *save_tstate = PyThreadState_Swap(NULL);
20102014
// XXX Possible GILState issues?
2011-
PyThreadState *tstate = Py_NewInterpreter();
2015+
PyThreadState *tstate = _Py_NewInterpreter(isolated);
20122016
PyThreadState_Swap(save_tstate);
20132017
if (tstate == NULL) {
20142018
/* Since no new thread state was created, there is no exception to
@@ -2547,8 +2551,8 @@ channel__channel_id(PyObject *self, PyObject *args, PyObject *kwds)
25472551
}
25482552

25492553
static PyMethodDef module_functions[] = {
2550-
{"create", (PyCFunction)interp_create,
2551-
METH_VARARGS, create_doc},
2554+
{"create", (PyCFunction)(void(*)(void))interp_create,
2555+
METH_VARARGS | METH_KEYWORDS, create_doc},
25522556
{"destroy", (PyCFunction)(void(*)(void))interp_destroy,
25532557
METH_VARARGS | METH_KEYWORDS, destroy_doc},
25542558
{"list_all", interp_list_all,

0 commit comments

Comments
 (0)