bpo-42260: Add _PyInterpreterState_SetConfig() (GH-23158)

* Inline _PyInterpreterState_SetConfig(): replace it with
  _PyConfig_Copy().
* Add _PyErr_SetFromPyStatus()
* Add _PyInterpreterState_GetConfigCopy()
* Add a new _PyInterpreterState_SetConfig() function.
* Add an unit which gets, modifies, and sets the config.
This commit is contained in:
Victor Stinner 2020-11-05 00:45:56 +01:00 committed by GitHub
parent 100964e031
commit 048a35659a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 189 additions and 16 deletions

View File

@ -128,6 +128,8 @@ PyStatus
Initialization error with a message.
*err_msg* must not be ``NULL``.
.. c:function:: PyStatus PyStatus_NoMemory(void)
Memory allocation failure (out of memory).

View File

@ -193,6 +193,36 @@ PyAPI_FUNC(void) _PyInterpreterState_SetEvalFrameFunc(
PyAPI_FUNC(const PyConfig*) _PyInterpreterState_GetConfig(PyInterpreterState *interp);
/* Get a copy of the current interpreter configuration.
Return 0 on success. Raise an exception and return -1 on error.
The caller must initialize 'config', using PyConfig_InitPythonConfig()
for example.
Python must be preinitialized to call this method.
The caller must hold the GIL. */
PyAPI_FUNC(int) _PyInterpreterState_GetConfigCopy(
struct PyConfig *config);
/* Set the configuration of the current interpreter.
This function should be called during or just after the Python
initialization.
Update the sys module with the new configuration. If the sys module was
modified directly after the Python initialization, these changes are lost.
Some configuration like faulthandler or warnoptions can be updated in the
configuration, but don't reconfigure Python (don't enable/disable
faulthandler and don't reconfigure warnings filters).
Return 0 on success. Raise an exception and return -1 on error.
The configuration should come from _PyInterpreterState_GetConfigCopy(). */
PyAPI_FUNC(int) _PyInterpreterState_SetConfig(
const struct PyConfig *config);
// Get the configuration of the currrent interpreter.
// The caller must hold the GIL.
PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void);

View File

@ -44,6 +44,8 @@ struct pyruntimestate;
#define _PyStatus_UPDATE_FUNC(err) \
do { err.func = _PyStatus_GET_FUNC(); } while (0)
PyObject* _PyErr_SetFromPyStatus(PyStatus status);
/* --- PyWideStringList ------------------------------------------------ */
#define _PyWideStringList_INIT (PyWideStringList){.length = 0, .items = NULL}

View File

@ -263,13 +263,7 @@ struct _is {
struct ast_state ast;
};
/* Used by _PyImport_Cleanup() */
extern void _PyInterpreterState_ClearModules(PyInterpreterState *interp);
extern PyStatus _PyInterpreterState_SetConfig(
PyInterpreterState *interp,
const PyConfig *config);
extern void _PyInterpreterState_Clear(PyThreadState *tstate);

View File

@ -1394,6 +1394,15 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
self.check_all_configs("test_init_warnoptions", config, preconfig,
api=API_PYTHON)
def test_init_set_config(self):
config = {
'_init_main': 0,
'bytes_warning': 2,
'warnoptions': ['error::BytesWarning'],
}
self.check_all_configs("test_init_set_config", config,
api=API_ISOLATED)
def test_get_argc_argv(self):
self.run_embedded_interpreter("test_get_argc_argv")
# ignore output

View File

@ -1526,6 +1526,55 @@ static int test_init_warnoptions(void)
}
static int tune_config(void)
{
PyConfig config;
PyConfig_InitPythonConfig(&config);
if (_PyInterpreterState_GetConfigCopy(&config) < 0) {
PyConfig_Clear(&config);
PyErr_Print();
return -1;
}
config.bytes_warning = 2;
if (_PyInterpreterState_SetConfig(&config) < 0) {
PyConfig_Clear(&config);
return -1;
}
PyConfig_Clear(&config);
return 0;
}
static int test_set_config(void)
{
// Initialize core
PyConfig config;
PyConfig_InitIsolatedConfig(&config);
config_set_string(&config, &config.program_name, PROGRAM_NAME);
config._init_main = 0;
config.bytes_warning = 0;
init_from_config_clear(&config);
// Tune the configuration using _PyInterpreterState_SetConfig()
if (tune_config() < 0) {
PyErr_Print();
return 1;
}
// Finish initialization: main part
PyStatus status = _Py_InitializeMain();
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}
dump_config();
Py_Finalize();
return 0;
}
static void configure_init_main(PyConfig *config)
{
wchar_t* argv[] = {
@ -1693,6 +1742,7 @@ static struct TestCase TestCases[] = {
{"test_init_setpath_config", test_init_setpath_config},
{"test_init_setpythonhome", test_init_setpythonhome},
{"test_init_warnoptions", test_init_warnoptions},
{"test_init_set_config", test_set_config},
{"test_run_main", test_run_main},
{"test_get_argc_argv", test_get_argc_argv},

View File

@ -242,8 +242,9 @@ PyStatus PyStatus_Ok(void)
PyStatus PyStatus_Error(const char *err_msg)
{
assert(err_msg != NULL);
return (PyStatus){._type = _PyStatus_TYPE_ERROR,
.err_msg = err_msg};
.err_msg = err_msg};
}
PyStatus PyStatus_NoMemory(void)
@ -262,6 +263,23 @@ int PyStatus_IsExit(PyStatus status)
int PyStatus_Exception(PyStatus status)
{ return _PyStatus_EXCEPTION(status); }
PyObject*
_PyErr_SetFromPyStatus(PyStatus status)
{
if (!_PyStatus_IS_ERROR(status)) {
PyErr_Format(PyExc_SystemError,
"%s() expects an error PyStatus",
_PyStatus_GET_FUNC());
}
else if (status.func) {
PyErr_Format(PyExc_ValueError, "%s: %s", status.func, status.err_msg);
}
else {
PyErr_Format(PyExc_ValueError, "%s", status.err_msg);
}
return NULL;
}
/* --- PyWideStringList ------------------------------------------------ */

View File

@ -428,6 +428,67 @@ _Py_SetLocaleFromEnv(int category)
}
static int
interpreter_set_config(const PyConfig *config)
{
PyThreadState *tstate = PyThreadState_Get();
PyStatus status = _PyConfig_Write(config, tstate->interp->runtime);
if (_PyStatus_EXCEPTION(status)) {
_PyErr_SetFromPyStatus(status);
return -1;
}
status = _PyConfig_Copy(&tstate->interp->config, config);
if (_PyStatus_EXCEPTION(status)) {
_PyErr_SetFromPyStatus(status);
return -1;
}
config = &tstate->interp->config;
if (config->_install_importlib && _Py_IsMainInterpreter(tstate)) {
status = _PyConfig_WritePathConfig(config);
if (_PyStatus_EXCEPTION(status)) {
_PyErr_SetFromPyStatus(status);
return -1;
}
}
// Update the sys module for the new configuration
if (_PySys_UpdateConfig(tstate) < 0) {
return -1;
}
return 0;
}
int
_PyInterpreterState_SetConfig(const PyConfig *src_config)
{
int res = -1;
PyConfig config;
PyConfig_InitPythonConfig(&config);
PyStatus status = _PyConfig_Copy(&config, src_config);
if (_PyStatus_EXCEPTION(status)) {
_PyErr_SetFromPyStatus(status);
goto done;
}
status = PyConfig_Read(&config);
if (_PyStatus_EXCEPTION(status)) {
_PyErr_SetFromPyStatus(status);
goto done;
}
res = interpreter_set_config(&config);
done:
PyConfig_Clear(&config);
return res;
}
/* Global initializations. Can be undone by Py_Finalize(). Don't
call this twice without an intervening Py_Finalize() call.
@ -462,7 +523,7 @@ pyinit_core_reconfigure(_PyRuntimeState *runtime,
return status;
}
status = _PyInterpreterState_SetConfig(interp, config);
status = _PyConfig_Copy(&interp->config, config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
@ -550,7 +611,7 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
return _PyStatus_ERR("can't make main interpreter");
}
PyStatus status = _PyInterpreterState_SetConfig(interp, config);
PyStatus status = _PyConfig_Copy(&interp->config, config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
@ -917,7 +978,7 @@ pyinit_core(_PyRuntimeState *runtime,
}
PyConfig config;
_PyConfig_InitCompatConfig(&config);
PyConfig_InitPythonConfig(&config);
status = _PyConfig_Copy(&config, src_config);
if (_PyStatus_EXCEPTION(status)) {
@ -1835,7 +1896,8 @@ new_interpreter(PyThreadState **tstate_p, int isolated_subinterpreter)
config = _PyInterpreterState_GetConfig(main_interp);
}
status = _PyInterpreterState_SetConfig(interp, config);
status = _PyConfig_Copy(&interp->config, config);
if (_PyStatus_EXCEPTION(status)) {
goto error;
}

View File

@ -778,7 +778,7 @@ PyState_RemoveModule(struct PyModuleDef* def)
return PyList_SetItem(interp->modules_by_index, index, Py_None);
}
/* Used by PyImport_Cleanup() */
// Used by finalize_modules()
void
_PyInterpreterState_ClearModules(PyInterpreterState *interp)
{
@ -1920,11 +1920,17 @@ _PyInterpreterState_GetConfig(PyInterpreterState *interp)
}
PyStatus
_PyInterpreterState_SetConfig(PyInterpreterState *interp,
const PyConfig *config)
int
_PyInterpreterState_GetConfigCopy(PyConfig *config)
{
return _PyConfig_Copy(&interp->config, config);
PyInterpreterState *interp = PyInterpreterState_Get();
PyStatus status = _PyConfig_Copy(config, &interp->config);
if (PyStatus_Exception(status)) {
_PyErr_SetFromPyStatus(status);
return -1;
}
return 0;
}