* :c:member:`PyConfig._init_main`: if set to 0,
:c:func:`Py_InitializeFromConfig` stops at the "Core" initialization phase.
+* :c:member:`PyConfig._isolated_interpreter`: if non-zero,
+ disallow threads, subprocesses and fork.
.. c:function:: PyStatus _Py_InitializeMain(void)
/* If equal to 0, stop Python initialization before the "main" phase */
int _init_main;
+
+ /* If non-zero, disallow threads, subprocesses, and fork.
+ Default: 0. */
+ int _isolated_interpreter;
} PyConfig;
PyAPI_FUNC(void) PyConfig_InitPythonConfig(PyConfig *config);
PyAPI_FUNC(int) _Py_LegacyLocaleDetected(int warn);
PyAPI_FUNC(char *) _Py_SetLocaleFromEnv(int category);
+PyAPI_FUNC(PyThreadState *) _Py_NewInterpreter(int isolated_subinterpreter);
+
#ifdef __cplusplus
}
#endif
self.assertEqual(out, 'it worked!')
def test_create_thread(self):
+ subinterp = interpreters.create(isolated=False)
script, file = _captured_script("""
import threading
def f():
t.join()
""")
with file:
- interpreters.run_string(self.id, script)
+ interpreters.run_string(subinterp, script)
out = file.read()
self.assertEqual(out, 'it worked!')
'check_hash_pycs_mode': 'default',
'pathconfig_warnings': 1,
'_init_main': 1,
+ '_isolated_interpreter': 0,
}
if MS_WINDOWS:
CONFIG_COMPAT.update({
'check_hash_pycs_mode': 'always',
'pathconfig_warnings': 0,
+
+ '_isolated_interpreter': 1,
}
self.check_all_configs("test_init_from_config", config, preconfig,
api=API_COMPAT)
--- /dev/null
+Add ``isolated=True`` keyword-only parameter to
+``_xxsubinterpreters.create()``. An isolated subinterpreter cannot spawn
+threads, spawn a child process or call ``os.fork()``.
return NULL;
}
+ PyInterpreterState *interp = PyInterpreterState_Get();
+ const PyConfig *config = _PyInterpreterState_GetConfig(interp);
+ if (config->_isolated_interpreter) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "subprocess not supported for isolated subinterpreters");
+ return NULL;
+ }
+
/* We need to call gc.disable() when we'll be calling preexec_fn */
if (preexec_fn != Py_None) {
PyObject *result;
"optional 3rd arg must be a dictionary");
return NULL;
}
+
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ if (interp->config._isolated_interpreter) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "thread is not supported for isolated subinterpreters");
+ return NULL;
+ }
+
boot = PyMem_NEW(struct bootstate, 1);
if (boot == NULL)
return PyErr_NoMemory();
return NULL;
}
+ PyInterpreterState *interp = PyInterpreterState_Get();
+ const PyConfig *config = _PyInterpreterState_GetConfig(interp);
+ if (config->_isolated_interpreter) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "subprocess not supported for isolated subinterpreters");
+ return NULL;
+ }
+
ZeroMemory(&si, sizeof(si));
si.StartupInfo.cb = sizeof(si);
}
static PyObject *
-interp_create(PyObject *self, PyObject *args)
+interp_create(PyObject *self, PyObject *args, PyObject *kwds)
{
- if (!PyArg_UnpackTuple(args, "create", 0, 0)) {
+
+ static char *kwlist[] = {"isolated", NULL};
+ int isolated = 1;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|$i:create", kwlist,
+ &isolated)) {
return NULL;
}
// Create and initialize the new interpreter.
PyThreadState *save_tstate = PyThreadState_Swap(NULL);
// XXX Possible GILState issues?
- PyThreadState *tstate = Py_NewInterpreter();
+ PyThreadState *tstate = _Py_NewInterpreter(isolated);
PyThreadState_Swap(save_tstate);
if (tstate == NULL) {
/* Since no new thread state was created, there is no exception to
}
static PyMethodDef module_functions[] = {
- {"create", (PyCFunction)interp_create,
- METH_VARARGS, create_doc},
+ {"create", (PyCFunction)(void(*)(void))interp_create,
+ METH_VARARGS | METH_KEYWORDS, create_doc},
{"destroy", (PyCFunction)(void(*)(void))interp_destroy,
METH_VARARGS | METH_KEYWORDS, destroy_doc},
{"list_all", interp_list_all,
/*[clinic end generated code: output=3626c81f98985d49 input=13c956413110eeaa]*/
{
pid_t pid;
-
- if (_PyInterpreterState_GET() != PyInterpreterState_Main()) {
- PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters");
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ if (interp->config._isolated_interpreter) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "fork not supported for isolated subinterpreters");
return NULL;
}
if (PySys_Audit("os.fork", NULL) < 0) {
Py_FrozenFlag = 0;
config.pathconfig_warnings = 0;
+ config._isolated_interpreter = 1;
+
init_from_config_clear(&config);
dump_config();
config->check_hash_pycs_mode = NULL;
config->pathconfig_warnings = -1;
config->_init_main = 1;
+ config->_isolated_interpreter = 0;
#ifdef MS_WINDOWS
config->legacy_windows_stdio = -1;
#endif
COPY_WSTR_ATTR(check_hash_pycs_mode);
COPY_ATTR(pathconfig_warnings);
COPY_ATTR(_init_main);
+ COPY_ATTR(_isolated_interpreter);
#undef COPY_ATTR
#undef COPY_WSTR_ATTR
SET_ITEM_WSTR(check_hash_pycs_mode);
SET_ITEM_INT(pathconfig_warnings);
SET_ITEM_INT(_init_main);
+ SET_ITEM_INT(_isolated_interpreter);
return dict;
*/
static PyStatus
-new_interpreter(PyThreadState **tstate_p)
+new_interpreter(PyThreadState **tstate_p, int isolated_subinterpreter)
{
PyStatus status;
if (_PyStatus_EXCEPTION(status)) {
goto error;
}
+ interp->config._isolated_interpreter = isolated_subinterpreter;
status = pycore_interp_init(tstate);
if (_PyStatus_EXCEPTION(status)) {
}
PyThreadState *
-Py_NewInterpreter(void)
+_Py_NewInterpreter(int isolated_subinterpreter)
{
PyThreadState *tstate = NULL;
- PyStatus status = new_interpreter(&tstate);
+ PyStatus status = new_interpreter(&tstate, isolated_subinterpreter);
if (_PyStatus_EXCEPTION(status)) {
Py_ExitStatusException(status);
}
}
+PyThreadState *
+Py_NewInterpreter(void)
+{
+ return _Py_NewInterpreter(0);
+}
+
/* Delete an interpreter and its last thread. This requires that the
given thread state is current, that the thread has no remaining
frames, and that it is its interpreter's only remaining thread.