summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2022-10-26 17:16:30 (GMT)
committerGitHub <noreply@github.com>2022-10-26 17:16:30 (GMT)
commitf32369480df54cb06884537ef16cb5a4143393f0 (patch)
treede906b0a7decf4c16d94d5311674ba19c278f9af /Modules
parent24c56b4642d467c194c57188d4a3b1848ee444c2 (diff)
downloadcpython-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.c3
-rw-r--r--Modules/_testcapimodule.c63
-rw-r--r--Modules/_testinternalcapi.c46
-rw-r--r--Modules/_threadmodule.c2
-rw-r--r--Modules/_winapi.c3
-rw-r--r--Modules/_xxsubinterpretersmodule.c7
-rw-r--r--Modules/posixmodule.c2
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;