Skip to content

Commit 9e1b828

Browse files
authored
bpo-42260: Compute the path config in the main init (GH-23211)
The path configuration is now computed in the "main" initialization. The core initialization no longer computes it. * Add _PyConfig_Read() function to read the configuration without computing the path configuration. * pyinit_core() no longer computes the path configuration: it is now computed by init_interp_main(). * The path configuration output members of PyConfig are now optional: * executable * base_executable * prefix * base_prefix * exec_prefix * base_exec_prefix * _PySys_UpdateConfig() now skips NULL strings in PyConfig. * _testembed: Rename test_set_config() to test_init_set_config() for consistency with other tests.
1 parent 38811d6 commit 9e1b828

File tree

6 files changed

+85
-74
lines changed

6 files changed

+85
-74
lines changed

Include/internal/pycore_initconfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ extern PyStatus _PyConfig_Copy(
152152
PyConfig *config,
153153
const PyConfig *config2);
154154
extern PyStatus _PyConfig_InitPathConfig(PyConfig *config);
155+
extern PyStatus _PyConfig_Read(PyConfig *config, int compute_path_config);
155156
extern PyStatus _PyConfig_Write(const PyConfig *config,
156157
struct pyruntimestate *runtime);
157158
extern PyStatus _PyConfig_SetPyArgv(

Lib/test/_test_embed_set_config.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,19 +100,19 @@ def test_set_invalid(self):
100100
'check_hash_pycs_mode',
101101
'program_name',
102102
'platlibdir',
103-
'executable',
104-
'base_executable',
105-
'prefix',
106-
'base_prefix',
107-
'exec_prefix',
108-
'base_exec_prefix',
109103
# optional wstr:
110104
# 'pythonpath_env'
111-
# 'home',
105+
# 'home'
112106
# 'pycache_prefix'
113107
# 'run_command'
114108
# 'run_module'
115109
# 'run_filename'
110+
# 'executable'
111+
# 'prefix'
112+
# 'exec_prefix'
113+
# 'base_executable'
114+
# 'base_prefix'
115+
# 'base_exec_prefix'
116116
):
117117
value_tests.append((key, invalid_wstr))
118118
type_tests.append((key, b'bytes'))
@@ -217,6 +217,18 @@ def test_pathconfig(self):
217217
self.set_config(base_executable="base_executable")
218218
self.assertEqual(sys._base_executable, "base_executable")
219219

220+
# When base_xxx is NULL, value is copied from xxxx
221+
self.set_config(
222+
executable='executable',
223+
prefix="prefix",
224+
exec_prefix="exec_prefix",
225+
base_executable=None,
226+
base_prefix=None,
227+
base_exec_prefix=None)
228+
self.assertEqual(sys._base_executable, "executable")
229+
self.assertEqual(sys.base_prefix, "prefix")
230+
self.assertEqual(sys.base_exec_prefix, "exec_prefix")
231+
220232
def test_path(self):
221233
self.set_config(module_search_paths_set=1,
222234
module_search_paths=['a', 'b', 'c'])

Programs/_testembed.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,7 +1547,7 @@ static int tune_config(void)
15471547
}
15481548

15491549

1550-
static int test_set_config(void)
1550+
static int test_init_set_config(void)
15511551
{
15521552
// Initialize core
15531553
PyConfig config;
@@ -1742,7 +1742,7 @@ static struct TestCase TestCases[] = {
17421742
{"test_init_setpath_config", test_init_setpath_config},
17431743
{"test_init_setpythonhome", test_init_setpythonhome},
17441744
{"test_init_warnoptions", test_init_warnoptions},
1745-
{"test_init_set_config", test_set_config},
1745+
{"test_init_set_config", test_init_set_config},
17461746
{"test_run_main", test_run_main},
17471747
{"test_get_argc_argv", test_get_argc_argv},
17481748

Python/initconfig.c

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -619,15 +619,6 @@ config_check_consistency(const PyConfig *config)
619619
assert(_PyWideStringList_CheckConsistency(&config->warnoptions));
620620
assert(_PyWideStringList_CheckConsistency(&config->module_search_paths));
621621
assert(config->module_search_paths_set >= 0);
622-
if (config->_install_importlib) {
623-
/* don't check config->module_search_paths */
624-
assert(config->executable != NULL);
625-
assert(config->base_executable != NULL);
626-
assert(config->prefix != NULL);
627-
assert(config->base_prefix != NULL);
628-
assert(config->exec_prefix != NULL);
629-
assert(config->base_exec_prefix != NULL);
630-
}
631622
assert(config->platlibdir != NULL);
632623
assert(config->filesystem_encoding != NULL);
633624
assert(config->filesystem_errors != NULL);
@@ -1297,24 +1288,15 @@ _PyConfig_FromDict(PyConfig *config, PyObject *dict)
12971288
GET_WSTR_OPT(home);
12981289
GET_WSTR(platlibdir);
12991290

1291+
// Path configuration output
13001292
GET_UINT(module_search_paths_set);
13011293
GET_WSTRLIST(module_search_paths);
1302-
if (config->_install_importlib) {
1303-
GET_WSTR(executable);
1304-
GET_WSTR(base_executable);
1305-
GET_WSTR(prefix);
1306-
GET_WSTR(base_prefix);
1307-
GET_WSTR(exec_prefix);
1308-
GET_WSTR(base_exec_prefix);
1309-
}
1310-
else {
1311-
GET_WSTR_OPT(executable);
1312-
GET_WSTR_OPT(base_executable);
1313-
GET_WSTR_OPT(prefix);
1314-
GET_WSTR_OPT(base_prefix);
1315-
GET_WSTR_OPT(exec_prefix);
1316-
GET_WSTR_OPT(base_exec_prefix);
1317-
}
1294+
GET_WSTR_OPT(executable);
1295+
GET_WSTR_OPT(base_executable);
1296+
GET_WSTR_OPT(prefix);
1297+
GET_WSTR_OPT(base_prefix);
1298+
GET_WSTR_OPT(exec_prefix);
1299+
GET_WSTR_OPT(base_exec_prefix);
13181300

13191301
GET_UINT(skip_source_first_line);
13201302
GET_WSTR_OPT(run_command);
@@ -2043,7 +2025,7 @@ config_init_fs_encoding(PyConfig *config, const PyPreConfig *preconfig)
20432025

20442026

20452027
static PyStatus
2046-
config_read(PyConfig *config)
2028+
config_read(PyConfig *config, int compute_path_config)
20472029
{
20482030
PyStatus status;
20492031
const PyPreConfig *preconfig = &_PyRuntime.preconfig;
@@ -2087,7 +2069,7 @@ config_read(PyConfig *config)
20872069
}
20882070
}
20892071

2090-
if (config->_install_importlib) {
2072+
if (compute_path_config && config->_install_importlib) {
20912073
status = _PyConfig_InitPathConfig(config);
20922074
if (_PyStatus_EXCEPTION(status)) {
20932075
return status;
@@ -2834,7 +2816,7 @@ PyConfig_SetWideStringList(PyConfig *config, PyWideStringList *list,
28342816
28352817
The only side effects are to modify config and to call _Py_SetArgcArgv(). */
28362818
PyStatus
2837-
PyConfig_Read(PyConfig *config)
2819+
_PyConfig_Read(PyConfig *config, int compute_path_config)
28382820
{
28392821
PyStatus status;
28402822

@@ -2877,7 +2859,7 @@ PyConfig_Read(PyConfig *config)
28772859
goto done;
28782860
}
28792861

2880-
status = config_read(config);
2862+
status = config_read(config, compute_path_config);
28812863
if (_PyStatus_EXCEPTION(status)) {
28822864
goto done;
28832865
}
@@ -2892,6 +2874,13 @@ PyConfig_Read(PyConfig *config)
28922874
}
28932875

28942876

2877+
PyStatus
2878+
PyConfig_Read(PyConfig *config)
2879+
{
2880+
return _PyConfig_Read(config, 1);
2881+
}
2882+
2883+
28952884
PyObject*
28962885
_Py_GetConfigsAsDict(void)
28972886
{

Python/pylifecycle.c

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -429,25 +429,20 @@ _Py_SetLocaleFromEnv(int category)
429429

430430

431431
static int
432-
interpreter_set_config(const PyConfig *config)
432+
interpreter_update_config(PyThreadState *tstate, int only_update_path_config)
433433
{
434-
PyThreadState *tstate = PyThreadState_Get();
434+
const PyConfig *config = &tstate->interp->config;
435435

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;
436+
if (!only_update_path_config) {
437+
PyStatus status = _PyConfig_Write(config, tstate->interp->runtime);
438+
if (_PyStatus_EXCEPTION(status)) {
439+
_PyErr_SetFromPyStatus(status);
440+
return -1;
441+
}
446442
}
447-
config = &tstate->interp->config;
448443

449-
if (config->_install_importlib && _Py_IsMainInterpreter(tstate)) {
450-
status = _PyConfig_WritePathConfig(config);
444+
if (_Py_IsMainInterpreter(tstate)) {
445+
PyStatus status = _PyConfig_WritePathConfig(config);
451446
if (_PyStatus_EXCEPTION(status)) {
452447
_PyErr_SetFromPyStatus(status);
453448
return -1;
@@ -465,6 +460,7 @@ interpreter_set_config(const PyConfig *config)
465460
int
466461
_PyInterpreterState_SetConfig(const PyConfig *src_config)
467462
{
463+
PyThreadState *tstate = PyThreadState_Get();
468464
int res = -1;
469465

470466
PyConfig config;
@@ -481,7 +477,13 @@ _PyInterpreterState_SetConfig(const PyConfig *src_config)
481477
goto done;
482478
}
483479

484-
res = interpreter_set_config(&config);
480+
status = _PyConfig_Copy(&tstate->interp->config, &config);
481+
if (_PyStatus_EXCEPTION(status)) {
482+
_PyErr_SetFromPyStatus(status);
483+
goto done;
484+
}
485+
486+
res = interpreter_update_config(tstate, 0);
485487

486488
done:
487489
PyConfig_Clear(&config);
@@ -763,13 +765,6 @@ pycore_init_import_warnings(PyThreadState *tstate, PyObject *sysmod)
763765

764766
const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp);
765767
if (config->_install_importlib) {
766-
if (_Py_IsMainInterpreter(tstate)) {
767-
status = _PyConfig_WritePathConfig(config);
768-
if (_PyStatus_EXCEPTION(status)) {
769-
return status;
770-
}
771-
}
772-
773768
/* This call sets up builtin and frozen import support */
774769
status = init_importlib(tstate, sysmod);
775770
if (_PyStatus_EXCEPTION(status)) {
@@ -985,7 +980,9 @@ pyinit_core(_PyRuntimeState *runtime,
985980
goto done;
986981
}
987982

988-
status = PyConfig_Read(&config);
983+
// Read the configuration, but don't compute the path configuration
984+
// (it is computed in the main init).
985+
status = _PyConfig_Read(&config, 0);
989986
if (_PyStatus_EXCEPTION(status)) {
990987
goto done;
991988
}
@@ -1012,8 +1009,8 @@ pyinit_core(_PyRuntimeState *runtime,
10121009
static PyStatus
10131010
pyinit_main_reconfigure(PyThreadState *tstate)
10141011
{
1015-
if (_PySys_UpdateConfig(tstate) < 0) {
1016-
return _PyStatus_ERR("fail to update sys for the new conf");
1012+
if (interpreter_update_config(tstate, 0) < 0) {
1013+
return _PyStatus_ERR("fail to reconfigure Python");
10171014
}
10181015
return _PyStatus_OK();
10191016
}
@@ -1041,14 +1038,20 @@ init_interp_main(PyThreadState *tstate)
10411038
return _PyStatus_OK();
10421039
}
10431040

1041+
// Compute the path configuration
1042+
status = _PyConfig_InitPathConfig(&interp->config);
1043+
if (_PyStatus_EXCEPTION(status)) {
1044+
return status;
1045+
}
1046+
10441047
if (is_main_interp) {
10451048
if (_PyTime_Init() < 0) {
10461049
return _PyStatus_ERR("can't initialize time");
10471050
}
10481051
}
10491052

1050-
if (_PySys_UpdateConfig(tstate) < 0) {
1051-
return _PyStatus_ERR("can't finish initializing sys");
1053+
if (interpreter_update_config(tstate, 1) < 0) {
1054+
return _PyStatus_ERR("failed to update the Python config");
10521055
}
10531056

10541057
status = init_importlib_external(tstate);

Python/sysmodule.c

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2922,17 +2922,22 @@ _PySys_UpdateConfig(PyThreadState *tstate)
29222922
#define SET_SYS_FROM_WSTR(KEY, VALUE) \
29232923
SET_SYS(KEY, PyUnicode_FromWideChar(VALUE, -1));
29242924

2925+
#define COPY_WSTR(SYS_ATTR, WSTR) \
2926+
if (WSTR != NULL) { \
2927+
SET_SYS_FROM_WSTR(SYS_ATTR, WSTR); \
2928+
}
2929+
29252930
if (config->module_search_paths_set) {
29262931
COPY_LIST("path", config->module_search_paths);
29272932
}
29282933

2929-
SET_SYS_FROM_WSTR("executable", config->executable);
2930-
SET_SYS_FROM_WSTR("_base_executable", config->base_executable);
2931-
SET_SYS_FROM_WSTR("prefix", config->prefix);
2932-
SET_SYS_FROM_WSTR("base_prefix", config->base_prefix);
2933-
SET_SYS_FROM_WSTR("exec_prefix", config->exec_prefix);
2934-
SET_SYS_FROM_WSTR("base_exec_prefix", config->base_exec_prefix);
2935-
SET_SYS_FROM_WSTR("platlibdir", config->platlibdir);
2934+
COPY_WSTR("executable", config->executable);
2935+
COPY_WSTR("_base_executable", config->base_executable);
2936+
COPY_WSTR("prefix", config->prefix);
2937+
COPY_WSTR("base_prefix", config->base_prefix);
2938+
COPY_WSTR("exec_prefix", config->exec_prefix);
2939+
COPY_WSTR("base_exec_prefix", config->base_exec_prefix);
2940+
COPY_WSTR("platlibdir", config->platlibdir);
29362941

29372942
if (config->pycache_prefix != NULL) {
29382943
SET_SYS_FROM_WSTR("pycache_prefix", config->pycache_prefix);
@@ -2946,8 +2951,9 @@ _PySys_UpdateConfig(PyThreadState *tstate)
29462951

29472952
SET_SYS("_xoptions", sys_create_xoptions_dict(config));
29482953

2949-
#undef COPY_LIST
29502954
#undef SET_SYS_FROM_WSTR
2955+
#undef COPY_LIST
2956+
#undef COPY_WSTR
29512957

29522958
// sys.flags
29532959
PyObject *flags = _PySys_GetObject(tstate, "flags"); // borrowed ref

0 commit comments

Comments
 (0)