diff options
author | Eric Snow <ericsnowcurrently@gmail.com> | 2022-10-26 17:16:30 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-26 17:16:30 (GMT) |
commit | f32369480df54cb06884537ef16cb5a4143393f0 (patch) | |
tree | de906b0a7decf4c16d94d5311674ba19c278f9af /Modules | |
parent | 24c56b4642d467c194c57188d4a3b1848ee444c2 (diff) | |
download | cpython-f32369480df54cb06884537ef16cb5a4143393f0.zip cpython-f32369480df54cb06884537ef16cb5a4143393f0.tar.gz cpython-f32369480df54cb06884537ef16cb5a4143393f0.tar.bz2 |
gh-98608: Change _Py_NewInterpreter() to _Py_NewInterpreterFromConfig() (gh-98609)
(see https://github.com/python/cpython/issues/98608)
This change does the following:
1. change the argument to a new `_PyInterpreterConfig` struct
2. rename the function to `_Py_NewInterpreterFromConfig()`, inspired by `Py_InitializeFromConfig()` (takes a `_PyInterpreterConfig` instead of `isolated_subinterpreter`)
3. split up the boolean `isolated_subinterpreter` into the corresponding multiple granular settings
* allow_fork
* allow_subprocess
* allow_threads
4. add `PyInterpreterState.feature_flags` to store those settings
5. add a function for checking if a feature is enabled on an opaque `PyInterpreterState *`
6. drop `PyConfig._isolated_interpreter`
The existing default (see `Py_NewInterpeter()` and `Py_Initialize*()`) allows fork, subprocess, and threads and the optional "isolated" interpreter (see the `_xxsubinterpreters` module) disables all three. None of that changes here; the defaults are preserved.
Note that the given `_PyInterpreterConfig` will not be used outside `_Py_NewInterpreterFromConfig()`, nor preserved. This contrasts with how `PyConfig` is currently preserved, used, and even modified outside `Py_InitializeFromConfig()`. I'd rather just avoid that mess from the start for `_PyInterpreterConfig`. We can preserve it later if we find an actual need.
This change allows us to follow up with a number of improvements (e.g. stop disallowing subprocess and support disallowing exec instead).
(Note that this PR adds "private" symbols. We'll probably make them public, and add docs, in a separate change.)
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_posixsubprocess.c | 3 | ||||
-rw-r--r-- | Modules/_testcapimodule.c | 63 | ||||
-rw-r--r-- | Modules/_testinternalcapi.c | 46 | ||||
-rw-r--r-- | Modules/_threadmodule.c | 2 | ||||
-rw-r--r-- | Modules/_winapi.c | 3 | ||||
-rw-r--r-- | Modules/_xxsubinterpretersmodule.c | 7 | ||||
-rw-r--r-- | Modules/posixmodule.c | 2 |
7 files changed, 119 insertions, 7 deletions
diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 44e60d7..8275b11 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -842,8 +842,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args) } PyInterpreterState *interp = PyInterpreterState_Get(); - const PyConfig *config = _PyInterpreterState_GetConfig(interp); - if (config->_isolated_interpreter) { + if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_SUBPROCESS)) { PyErr_SetString(PyExc_RuntimeError, "subprocess not supported for isolated subinterpreters"); return NULL; diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index e792a2c..fdf2f20 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3225,6 +3225,66 @@ run_in_subinterp(PyObject *self, PyObject *args) return PyLong_FromLong(r); } +/* To run some code in a sub-interpreter. */ +static PyObject * +run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs) +{ + const char *code; + int allow_fork = -1; + int allow_subprocess = -1; + int allow_threads = -1; + int r; + PyThreadState *substate, *mainstate; + /* only initialise 'cflags.cf_flags' to test backwards compatibility */ + PyCompilerFlags cflags = {0}; + + static char *kwlist[] = {"code", + "allow_fork", "allow_subprocess", "allow_threads", + NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "s$ppp:run_in_subinterp_with_config", kwlist, + &code, &allow_fork, &allow_subprocess, &allow_threads)) { + return NULL; + } + if (allow_fork < 0) { + PyErr_SetString(PyExc_ValueError, "missing allow_fork"); + return NULL; + } + if (allow_subprocess < 0) { + PyErr_SetString(PyExc_ValueError, "missing allow_subprocess"); + return NULL; + } + if (allow_threads < 0) { + PyErr_SetString(PyExc_ValueError, "missing allow_threads"); + return NULL; + } + + mainstate = PyThreadState_Get(); + + PyThreadState_Swap(NULL); + + const _PyInterpreterConfig config = { + .allow_fork = allow_fork, + .allow_subprocess = allow_subprocess, + .allow_threads = allow_threads, + }; + substate = _Py_NewInterpreterFromConfig(&config); + if (substate == NULL) { + /* Since no new thread state was created, there is no exception to + propagate; raise a fresh one after swapping in the old thread + state. */ + PyThreadState_Swap(mainstate); + PyErr_SetString(PyExc_RuntimeError, "sub-interpreter creation failed"); + return NULL; + } + r = PyRun_SimpleStringFlags(code, &cflags); + Py_EndInterpreter(substate); + + PyThreadState_Swap(mainstate); + + return PyLong_FromLong(r); +} + static int check_time_rounding(int round) { @@ -5998,6 +6058,9 @@ static PyMethodDef TestMethods[] = { METH_NOARGS}, {"crash_no_current_thread", crash_no_current_thread, METH_NOARGS}, {"run_in_subinterp", run_in_subinterp, METH_VARARGS}, + {"run_in_subinterp_with_config", + _PyCFunction_CAST(run_in_subinterp_with_config), + METH_VARARGS | METH_KEYWORDS}, {"pytime_object_to_time_t", test_pytime_object_to_time_t, METH_VARARGS}, {"pytime_object_to_timeval", test_pytime_object_to_timeval, METH_VARARGS}, {"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS}, diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 5724bd5..d74f928 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -550,6 +550,51 @@ _testinternalcapi_optimize_cfg_impl(PyObject *module, PyObject *instructions, } +static PyObject * +get_interp_settings(PyObject *self, PyObject *args) +{ + int interpid = -1; + if (!PyArg_ParseTuple(args, "|i:get_interp_settings", &interpid)) { + return NULL; + } + + PyInterpreterState *interp = NULL; + if (interpid < 0) { + PyThreadState *tstate = _PyThreadState_GET(); + interp = tstate ? tstate->interp : _PyInterpreterState_Main(); + } + else if (interpid == 0) { + interp = _PyInterpreterState_Main(); + } + else { + PyErr_Format(PyExc_NotImplementedError, + "%zd", interpid); + return NULL; + } + assert(interp != NULL); + + PyObject *settings = PyDict_New(); + if (settings == NULL) { + return NULL; + } + + /* Add the feature flags. */ + PyObject *flags = PyLong_FromUnsignedLong(interp->feature_flags); + if (flags == NULL) { + Py_DECREF(settings); + return NULL; + } + int res = PyDict_SetItemString(settings, "feature_flags", flags); + Py_DECREF(flags); + if (res != 0) { + Py_DECREF(settings); + return NULL; + } + + return settings; +} + + static PyMethodDef TestMethods[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -569,6 +614,7 @@ static PyMethodDef TestMethods[] = { {"set_eval_frame_default", set_eval_frame_default, METH_NOARGS, NULL}, {"set_eval_frame_record", set_eval_frame_record, METH_O, NULL}, _TESTINTERNALCAPI_OPTIMIZE_CFG_METHODDEF + {"get_interp_settings", get_interp_settings, METH_VARARGS, NULL}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 4ac90dc..93b3b8d 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1128,7 +1128,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) } PyInterpreterState *interp = _PyInterpreterState_GET(); - if (interp->config._isolated_interpreter) { + if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_THREADS)) { PyErr_SetString(PyExc_RuntimeError, "thread is not supported for isolated subinterpreters"); return NULL; diff --git a/Modules/_winapi.c b/Modules/_winapi.c index 4845b4e..2a916cc 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -1090,8 +1090,7 @@ _winapi_CreateProcess_impl(PyObject *module, } PyInterpreterState *interp = PyInterpreterState_Get(); - const PyConfig *config = _PyInterpreterState_GetConfig(interp); - if (config->_isolated_interpreter) { + if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_SUBPROCESS)) { PyErr_SetString(PyExc_RuntimeError, "subprocess not supported for isolated subinterpreters"); return NULL; diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index f40601a..f38de57 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -2003,8 +2003,13 @@ interp_create(PyObject *self, PyObject *args, PyObject *kwds) // Create and initialize the new interpreter. PyThreadState *save_tstate = _PyThreadState_GET(); + const _PyInterpreterConfig config = { + .allow_fork = !isolated, + .allow_subprocess = !isolated, + .allow_threads = !isolated, + }; // XXX Possible GILState issues? - PyThreadState *tstate = _Py_NewInterpreter(isolated); + PyThreadState *tstate = _Py_NewInterpreterFromConfig(&config); PyThreadState_Swap(save_tstate); if (tstate == NULL) { /* Since no new thread state was created, there is no exception to diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 56ea319..a5eb866 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -6760,7 +6760,7 @@ os_fork_impl(PyObject *module) { pid_t pid; PyInterpreterState *interp = _PyInterpreterState_GET(); - if (interp->config._isolated_interpreter) { + if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_FORK)) { PyErr_SetString(PyExc_RuntimeError, "fork not supported for isolated subinterpreters"); return NULL; |