Skip to content

Commit 08df9b6

Browse files
vstinneradorilson
authored andcommitted
bpo-42260: Add _PyInterpreterState_SetConfig() (pythonGH-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.
1 parent 979b9de commit 08df9b6

File tree

9 files changed

+189
-16
lines changed

9 files changed

+189
-16
lines changed

Doc/c-api/init_config.rst

+2
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ PyStatus
128128
129129
Initialization error with a message.
130130
131+
*err_msg* must not be ``NULL``.
132+
131133
.. c:function:: PyStatus PyStatus_NoMemory(void)
132134
133135
Memory allocation failure (out of memory).

Include/cpython/pystate.h

+30
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,36 @@ PyAPI_FUNC(void) _PyInterpreterState_SetEvalFrameFunc(
193193

194194
PyAPI_FUNC(const PyConfig*) _PyInterpreterState_GetConfig(PyInterpreterState *interp);
195195

196+
/* Get a copy of the current interpreter configuration.
197+
198+
Return 0 on success. Raise an exception and return -1 on error.
199+
200+
The caller must initialize 'config', using PyConfig_InitPythonConfig()
201+
for example.
202+
203+
Python must be preinitialized to call this method.
204+
The caller must hold the GIL. */
205+
PyAPI_FUNC(int) _PyInterpreterState_GetConfigCopy(
206+
struct PyConfig *config);
207+
208+
/* Set the configuration of the current interpreter.
209+
210+
This function should be called during or just after the Python
211+
initialization.
212+
213+
Update the sys module with the new configuration. If the sys module was
214+
modified directly after the Python initialization, these changes are lost.
215+
216+
Some configuration like faulthandler or warnoptions can be updated in the
217+
configuration, but don't reconfigure Python (don't enable/disable
218+
faulthandler and don't reconfigure warnings filters).
219+
220+
Return 0 on success. Raise an exception and return -1 on error.
221+
222+
The configuration should come from _PyInterpreterState_GetConfigCopy(). */
223+
PyAPI_FUNC(int) _PyInterpreterState_SetConfig(
224+
const struct PyConfig *config);
225+
196226
// Get the configuration of the currrent interpreter.
197227
// The caller must hold the GIL.
198228
PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void);

Include/internal/pycore_initconfig.h

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ struct pyruntimestate;
4444
#define _PyStatus_UPDATE_FUNC(err) \
4545
do { err.func = _PyStatus_GET_FUNC(); } while (0)
4646

47+
PyObject* _PyErr_SetFromPyStatus(PyStatus status);
48+
4749
/* --- PyWideStringList ------------------------------------------------ */
4850

4951
#define _PyWideStringList_INIT (PyWideStringList){.length = 0, .items = NULL}

Include/internal/pycore_interp.h

-6
Original file line numberDiff line numberDiff line change
@@ -263,13 +263,7 @@ struct _is {
263263
struct ast_state ast;
264264
};
265265

266-
/* Used by _PyImport_Cleanup() */
267266
extern void _PyInterpreterState_ClearModules(PyInterpreterState *interp);
268-
269-
extern PyStatus _PyInterpreterState_SetConfig(
270-
PyInterpreterState *interp,
271-
const PyConfig *config);
272-
273267
extern void _PyInterpreterState_Clear(PyThreadState *tstate);
274268

275269

Lib/test/test_embed.py

+9
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,15 @@ def test_init_warnoptions(self):
13941394
self.check_all_configs("test_init_warnoptions", config, preconfig,
13951395
api=API_PYTHON)
13961396

1397+
def test_init_set_config(self):
1398+
config = {
1399+
'_init_main': 0,
1400+
'bytes_warning': 2,
1401+
'warnoptions': ['error::BytesWarning'],
1402+
}
1403+
self.check_all_configs("test_init_set_config", config,
1404+
api=API_ISOLATED)
1405+
13971406
def test_get_argc_argv(self):
13981407
self.run_embedded_interpreter("test_get_argc_argv")
13991408
# ignore output

Programs/_testembed.c

+50
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,55 @@ static int test_init_warnoptions(void)
15261526
}
15271527

15281528

1529+
static int tune_config(void)
1530+
{
1531+
PyConfig config;
1532+
PyConfig_InitPythonConfig(&config);
1533+
if (_PyInterpreterState_GetConfigCopy(&config) < 0) {
1534+
PyConfig_Clear(&config);
1535+
PyErr_Print();
1536+
return -1;
1537+
}
1538+
1539+
config.bytes_warning = 2;
1540+
1541+
if (_PyInterpreterState_SetConfig(&config) < 0) {
1542+
PyConfig_Clear(&config);
1543+
return -1;
1544+
}
1545+
PyConfig_Clear(&config);
1546+
return 0;
1547+
}
1548+
1549+
1550+
static int test_set_config(void)
1551+
{
1552+
// Initialize core
1553+
PyConfig config;
1554+
PyConfig_InitIsolatedConfig(&config);
1555+
config_set_string(&config, &config.program_name, PROGRAM_NAME);
1556+
config._init_main = 0;
1557+
config.bytes_warning = 0;
1558+
init_from_config_clear(&config);
1559+
1560+
// Tune the configuration using _PyInterpreterState_SetConfig()
1561+
if (tune_config() < 0) {
1562+
PyErr_Print();
1563+
return 1;
1564+
}
1565+
1566+
// Finish initialization: main part
1567+
PyStatus status = _Py_InitializeMain();
1568+
if (PyStatus_Exception(status)) {
1569+
Py_ExitStatusException(status);
1570+
}
1571+
1572+
dump_config();
1573+
Py_Finalize();
1574+
return 0;
1575+
}
1576+
1577+
15291578
static void configure_init_main(PyConfig *config)
15301579
{
15311580
wchar_t* argv[] = {
@@ -1693,6 +1742,7 @@ static struct TestCase TestCases[] = {
16931742
{"test_init_setpath_config", test_init_setpath_config},
16941743
{"test_init_setpythonhome", test_init_setpythonhome},
16951744
{"test_init_warnoptions", test_init_warnoptions},
1745+
{"test_init_set_config", test_set_config},
16961746
{"test_run_main", test_run_main},
16971747
{"test_get_argc_argv", test_get_argc_argv},
16981748

Python/initconfig.c

+19-1
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,9 @@ PyStatus PyStatus_Ok(void)
242242

243243
PyStatus PyStatus_Error(const char *err_msg)
244244
{
245+
assert(err_msg != NULL);
245246
return (PyStatus){._type = _PyStatus_TYPE_ERROR,
246-
.err_msg = err_msg};
247+
.err_msg = err_msg};
247248
}
248249

249250
PyStatus PyStatus_NoMemory(void)
@@ -262,6 +263,23 @@ int PyStatus_IsExit(PyStatus status)
262263
int PyStatus_Exception(PyStatus status)
263264
{ return _PyStatus_EXCEPTION(status); }
264265

266+
PyObject*
267+
_PyErr_SetFromPyStatus(PyStatus status)
268+
{
269+
if (!_PyStatus_IS_ERROR(status)) {
270+
PyErr_Format(PyExc_SystemError,
271+
"%s() expects an error PyStatus",
272+
_PyStatus_GET_FUNC());
273+
}
274+
else if (status.func) {
275+
PyErr_Format(PyExc_ValueError, "%s: %s", status.func, status.err_msg);
276+
}
277+
else {
278+
PyErr_Format(PyExc_ValueError, "%s", status.err_msg);
279+
}
280+
return NULL;
281+
}
282+
265283

266284
/* --- PyWideStringList ------------------------------------------------ */
267285

Python/pylifecycle.c

+66-4
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,67 @@ _Py_SetLocaleFromEnv(int category)
428428
}
429429

430430

431+
static int
432+
interpreter_set_config(const PyConfig *config)
433+
{
434+
PyThreadState *tstate = PyThreadState_Get();
435+
436+
PyStatus status = _PyConfig_Write(config, tstate->interp->runtime);
437+
if (_PyStatus_EXCEPTION(status)) {
438+
_PyErr_SetFromPyStatus(status);
439+
return -1;
440+
}
441+
442+
status = _PyConfig_Copy(&tstate->interp->config, config);
443+
if (_PyStatus_EXCEPTION(status)) {
444+
_PyErr_SetFromPyStatus(status);
445+
return -1;
446+
}
447+
config = &tstate->interp->config;
448+
449+
if (config->_install_importlib && _Py_IsMainInterpreter(tstate)) {
450+
status = _PyConfig_WritePathConfig(config);
451+
if (_PyStatus_EXCEPTION(status)) {
452+
_PyErr_SetFromPyStatus(status);
453+
return -1;
454+
}
455+
}
456+
457+
// Update the sys module for the new configuration
458+
if (_PySys_UpdateConfig(tstate) < 0) {
459+
return -1;
460+
}
461+
return 0;
462+
}
463+
464+
465+
int
466+
_PyInterpreterState_SetConfig(const PyConfig *src_config)
467+
{
468+
int res = -1;
469+
470+
PyConfig config;
471+
PyConfig_InitPythonConfig(&config);
472+
PyStatus status = _PyConfig_Copy(&config, src_config);
473+
if (_PyStatus_EXCEPTION(status)) {
474+
_PyErr_SetFromPyStatus(status);
475+
goto done;
476+
}
477+
478+
status = PyConfig_Read(&config);
479+
if (_PyStatus_EXCEPTION(status)) {
480+
_PyErr_SetFromPyStatus(status);
481+
goto done;
482+
}
483+
484+
res = interpreter_set_config(&config);
485+
486+
done:
487+
PyConfig_Clear(&config);
488+
return res;
489+
}
490+
491+
431492
/* Global initializations. Can be undone by Py_Finalize(). Don't
432493
call this twice without an intervening Py_Finalize() call.
433494
@@ -462,7 +523,7 @@ pyinit_core_reconfigure(_PyRuntimeState *runtime,
462523
return status;
463524
}
464525

465-
status = _PyInterpreterState_SetConfig(interp, config);
526+
status = _PyConfig_Copy(&interp->config, config);
466527
if (_PyStatus_EXCEPTION(status)) {
467528
return status;
468529
}
@@ -550,7 +611,7 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
550611
return _PyStatus_ERR("can't make main interpreter");
551612
}
552613

553-
PyStatus status = _PyInterpreterState_SetConfig(interp, config);
614+
PyStatus status = _PyConfig_Copy(&interp->config, config);
554615
if (_PyStatus_EXCEPTION(status)) {
555616
return status;
556617
}
@@ -917,7 +978,7 @@ pyinit_core(_PyRuntimeState *runtime,
917978
}
918979

919980
PyConfig config;
920-
_PyConfig_InitCompatConfig(&config);
981+
PyConfig_InitPythonConfig(&config);
921982

922983
status = _PyConfig_Copy(&config, src_config);
923984
if (_PyStatus_EXCEPTION(status)) {
@@ -1835,7 +1896,8 @@ new_interpreter(PyThreadState **tstate_p, int isolated_subinterpreter)
18351896
config = _PyInterpreterState_GetConfig(main_interp);
18361897
}
18371898

1838-
status = _PyInterpreterState_SetConfig(interp, config);
1899+
1900+
status = _PyConfig_Copy(&interp->config, config);
18391901
if (_PyStatus_EXCEPTION(status)) {
18401902
goto error;
18411903
}

Python/pystate.c

+11-5
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ PyState_RemoveModule(struct PyModuleDef* def)
778778
return PyList_SetItem(interp->modules_by_index, index, Py_None);
779779
}
780780

781-
/* Used by PyImport_Cleanup() */
781+
// Used by finalize_modules()
782782
void
783783
_PyInterpreterState_ClearModules(PyInterpreterState *interp)
784784
{
@@ -1920,11 +1920,17 @@ _PyInterpreterState_GetConfig(PyInterpreterState *interp)
19201920
}
19211921

19221922

1923-
PyStatus
1924-
_PyInterpreterState_SetConfig(PyInterpreterState *interp,
1925-
const PyConfig *config)
1923+
int
1924+
_PyInterpreterState_GetConfigCopy(PyConfig *config)
19261925
{
1927-
return _PyConfig_Copy(&interp->config, config);
1926+
PyInterpreterState *interp = PyInterpreterState_Get();
1927+
1928+
PyStatus status = _PyConfig_Copy(config, &interp->config);
1929+
if (PyStatus_Exception(status)) {
1930+
_PyErr_SetFromPyStatus(status);
1931+
return -1;
1932+
}
1933+
return 0;
19281934
}
19291935

19301936

0 commit comments

Comments
 (0)