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

@ -213,10 +213,6 @@ typedef struct PyConfig {
// 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;
// If non-zero, we believe we're running from a source tree.
int _is_python_build;
} PyConfig;
@ -245,6 +241,21 @@ PyAPI_FUNC(PyStatus) PyConfig_SetWideStringList(PyConfig *config,
Py_ssize_t length, wchar_t **items);
/* --- PyInterpreterConfig ------------------------------------ */
typedef struct {
int allow_fork;
int allow_subprocess;
int allow_threads;
} _PyInterpreterConfig;
#define _PyInterpreterConfig_LEGACY_INIT \
{ \
.allow_fork = 1, \
.allow_subprocess = 1, \
.allow_threads = 1, \
}
/* --- Helper functions --------------------------------------- */
/* Get the original command line arguments, before Python modified them.

View file

@ -62,4 +62,5 @@ PyAPI_FUNC(int) _Py_CoerceLegacyLocale(int warn);
PyAPI_FUNC(int) _Py_LegacyLocaleDetected(int warn);
PyAPI_FUNC(char *) _Py_SetLocaleFromEnv(int category);
PyAPI_FUNC(PyThreadState *) _Py_NewInterpreter(int isolated_subinterpreter);
PyAPI_FUNC(PyThreadState *) _Py_NewInterpreterFromConfig(
const _PyInterpreterConfig *);

View file

@ -3,11 +3,38 @@
#endif
/*
Runtime Feature Flags
Each flag indicate whether or not a specific runtime feature
is available in a given context. For example, forking the process
might not be allowed in the current interpreter (i.e. os.fork() would fail).
*/
// We leave the first 10 for less-specific features.
/* Set if threads are allowed. */
#define Py_RTFLAGS_THREADS (1UL << 10)
/* Set if os.fork() is allowed. */
#define Py_RTFLAGS_FORK (1UL << 15)
/* Set if subprocesses are allowed. */
#define Py_RTFLAGS_SUBPROCESS (1UL << 16)
PyAPI_FUNC(int) _PyInterpreterState_HasFeature(PyInterpreterState *interp,
unsigned long feature);
/* private interpreter helpers */
PyAPI_FUNC(int) _PyInterpreterState_RequiresIDRef(PyInterpreterState *);
PyAPI_FUNC(void) _PyInterpreterState_RequireIDRef(PyInterpreterState *, int);
PyAPI_FUNC(PyObject *) _PyInterpreterState_GetMainModule(PyInterpreterState *);
/* State unique per thread */
/* Py_tracefunc return -1 when raising an exception, or 0 for success. */

View file

@ -143,6 +143,7 @@ struct _is {
#ifdef HAVE_DLOPEN
int dlopenflags;
#endif
unsigned long feature_flags;
PyObject *dict; /* Stores per-interpreter state */