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.)
This commit is contained in:
Eric Snow 2022-10-26 11:16:30 -06:00 committed by GitHub
parent 24c56b4642
commit f32369480d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 295 additions and 39 deletions

View file

@ -611,6 +611,22 @@ pycore_init_runtime(_PyRuntimeState *runtime,
}
static void
init_interp_settings(PyInterpreterState *interp, const _PyInterpreterConfig *config)
{
assert(interp->feature_flags == 0);
if (config->allow_fork) {
interp->feature_flags |= Py_RTFLAGS_FORK;
}
if (config->allow_subprocess) {
interp->feature_flags |= Py_RTFLAGS_SUBPROCESS;
}
if (config->allow_threads) {
interp->feature_flags |= Py_RTFLAGS_THREADS;
}
}
static PyStatus
init_interp_create_gil(PyThreadState *tstate)
{
@ -638,7 +654,7 @@ init_interp_create_gil(PyThreadState *tstate)
static PyStatus
pycore_create_interpreter(_PyRuntimeState *runtime,
const PyConfig *config,
const PyConfig *src_config,
PyThreadState **tstate_p)
{
/* Auto-thread-state API */
@ -653,11 +669,14 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
}
assert(_Py_IsMainInterpreter(interp));
status = _PyConfig_Copy(&interp->config, config);
status = _PyConfig_Copy(&interp->config, src_config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
const _PyInterpreterConfig config = _PyInterpreterConfig_LEGACY_INIT;
init_interp_settings(interp, &config);
PyThreadState *tstate = PyThreadState_New(interp);
if (tstate == NULL) {
return _PyStatus_ERR("can't make first thread");
@ -1961,7 +1980,7 @@ Py_Finalize(void)
*/
static PyStatus
new_interpreter(PyThreadState **tstate_p, int isolated_subinterpreter)
new_interpreter(PyThreadState **tstate_p, const _PyInterpreterConfig *config)
{
PyStatus status;
@ -1995,23 +2014,23 @@ new_interpreter(PyThreadState **tstate_p, int isolated_subinterpreter)
PyThreadState *save_tstate = PyThreadState_Swap(tstate);
/* Copy the current interpreter config into the new interpreter */
const PyConfig *config;
const PyConfig *src_config;
if (save_tstate != NULL) {
config = _PyInterpreterState_GetConfig(save_tstate->interp);
src_config = _PyInterpreterState_GetConfig(save_tstate->interp);
}
else
{
/* No current thread state, copy from the main interpreter */
PyInterpreterState *main_interp = _PyInterpreterState_Main();
config = _PyInterpreterState_GetConfig(main_interp);
src_config = _PyInterpreterState_GetConfig(main_interp);
}
status = _PyConfig_Copy(&interp->config, config);
status = _PyConfig_Copy(&interp->config, src_config);
if (_PyStatus_EXCEPTION(status)) {
goto error;
}
interp->config._isolated_interpreter = isolated_subinterpreter;
init_interp_settings(interp, config);
status = init_interp_create_gil(tstate);
if (_PyStatus_EXCEPTION(status)) {
@ -2045,21 +2064,21 @@ error:
}
PyThreadState *
_Py_NewInterpreter(int isolated_subinterpreter)
_Py_NewInterpreterFromConfig(const _PyInterpreterConfig *config)
{
PyThreadState *tstate = NULL;
PyStatus status = new_interpreter(&tstate, isolated_subinterpreter);
PyStatus status = new_interpreter(&tstate, config);
if (_PyStatus_EXCEPTION(status)) {
Py_ExitStatusException(status);
}
return tstate;
}
PyThreadState *
Py_NewInterpreter(void)
{
return _Py_NewInterpreter(0);
const _PyInterpreterConfig config = _PyInterpreterConfig_LEGACY_INIT;
return _Py_NewInterpreterFromConfig(&config);
}
/* Delete an interpreter and its last thread. This requires that the