From af5a895073c24637c094772b27526b94a12ec897 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 2 Dec 2017 10:11:32 +0100 Subject: [PATCH] bpo-32030: _PyPathConfig_Init() sets home and program_name (#4673) _PyPathConfig_Init() now also initialize home and program_name: * Rename existing _PyPathConfig_Init() to _PyPathConfig_Calculate(). Add a new _PyPathConfig_Init() function in pathconfig.c which handles the _Py_path_config variable and call _PyPathConfig_Calculate(). * Add home and program_name fields to _PyPathConfig.home * _PyPathConfig_Init() now initialize home and program_name from main_config * Py_SetProgramName(), Py_SetPythonHome() and Py_GetPythonHome() now calls Py_FatalError() on failure, instead of silently ignoring failures. * config_init_home() now gets directly _Py_path_config.home to only get the value set by Py_SetPythonHome(), or NULL if Py_SetPythonHome() was not called. * config_get_program_name() now gets directly _Py_path_config.program_name to only get the value set by Py_SetProgramName(), or NULL if Py_SetProgramName() was not called. * pymain_init_python() doesn't call Py_SetProgramName() anymore, _PyPathConfig_Init() now always sets the program name * Call _PyMainInterpreterConfig_Read() in pymain_parse_cmdline_envvars_impl() to control the memory allocator * C API documentation: it's no more safe to call Py_GetProgramName() before Py_Initialize(). --- Doc/c-api/init.rst | 5 +- Include/internal/pystate.h | 25 +++++- Include/pystate.h | 3 +- Modules/getpath.c | 17 +--- Modules/main.c | 38 +++++---- PC/getpathp.c | 21 +---- Python/pathconfig.c | 169 +++++++++++++++++++++++++++---------- Python/pylifecycle.c | 7 +- 8 files changed, 184 insertions(+), 101 deletions(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index a9927aba5e1..a3113a390fd 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -40,7 +40,6 @@ The following functions can be safely called before Python is initialized: * :c:func:`Py_GetCompiler` * :c:func:`Py_GetCopyright` * :c:func:`Py_GetPlatform` - * :c:func:`Py_GetProgramName` * :c:func:`Py_GetVersion` * Utilities: @@ -59,8 +58,8 @@ The following functions can be safely called before Python is initialized: The following functions **should not be called** before :c:func:`Py_Initialize`: :c:func:`Py_EncodeLocale`, :c:func:`Py_GetPath`, :c:func:`Py_GetPrefix`, :c:func:`Py_GetExecPrefix`, - :c:func:`Py_GetProgramFullPath`, :c:func:`Py_GetPythonHome` and - :c:func:`PyEval_InitThreads`. + :c:func:`Py_GetProgramFullPath`, :c:func:`Py_GetPythonHome`, + :c:func:`Py_GetProgramName` and :c:func:`PyEval_InitThreads`. .. _global-conf-vars: diff --git a/Include/internal/pystate.h b/Include/internal/pystate.h index 9d8531965f1..b9334212047 100644 --- a/Include/internal/pystate.h +++ b/Include/internal/pystate.h @@ -48,12 +48,35 @@ typedef struct { #endif /* Set by Py_SetPath(), or computed by _PyPathConfig_Init() */ wchar_t *module_search_path; + /* Python program name */ + wchar_t *program_name; + /* Set by Py_SetPythonHome() or PYTHONHOME environment variable */ + wchar_t *home; } _PyPathConfig; -#define _PyPathConfig_INIT {.module_search_path = NULL} +#ifdef MS_WINDOWS +#define _PyPathConfig_INIT \ + {.program_full_path = NULL, \ + .prefix = NULL, \ + .dll_path = NULL, \ + .module_search_path = NULL, \ + .program_name = NULL, \ + .home = NULL} +#else +#define _PyPathConfig_INIT \ + {.program_full_path = NULL, \ + .prefix = NULL, \ + .exec_prefix = NULL, \ + .module_search_path = NULL, \ + .program_name = NULL, \ + .home = NULL} +#endif PyAPI_DATA(_PyPathConfig) _Py_path_config; +PyAPI_FUNC(_PyInitError) _PyPathConfig_Calculate( + _PyPathConfig *config, + const _PyMainInterpreterConfig *main_config); PyAPI_FUNC(void) _PyPathConfig_Clear(_PyPathConfig *config); diff --git a/Include/pystate.h b/Include/pystate.h index 60d001c4926..1d8aab6d83f 100644 --- a/Include/pystate.h +++ b/Include/pystate.h @@ -72,7 +72,8 @@ typedef struct { (_PyMainInterpreterConfig){\ .install_signal_handlers = -1, \ .module_search_path_env = NULL, \ - .home = NULL} + .home = NULL, \ + .program_name = NULL} typedef struct _is { diff --git a/Modules/getpath.c b/Modules/getpath.c index 8554dbe2a2c..fc2b5442ce2 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -1008,16 +1008,10 @@ calculate_path_impl(const _PyMainInterpreterConfig *main_config, } -/* Initialize paths for Py_GetPath(), Py_GetPrefix(), Py_GetExecPrefix() - and Py_GetProgramFullPath() */ _PyInitError -_PyPathConfig_Init(const _PyMainInterpreterConfig *main_config) +_PyPathConfig_Calculate(_PyPathConfig *config, + const _PyMainInterpreterConfig *main_config) { - if (_Py_path_config.module_search_path) { - /* Already initialized */ - return _Py_INIT_OK(); - } - PyCalculatePath calculate; memset(&calculate, 0, sizeof(calculate)); @@ -1026,16 +1020,11 @@ _PyPathConfig_Init(const _PyMainInterpreterConfig *main_config) goto done; } - _PyPathConfig new_path_config; - memset(&new_path_config, 0, sizeof(new_path_config)); - - err = calculate_path_impl(main_config, &calculate, &new_path_config); + err = calculate_path_impl(main_config, &calculate, config); if (_Py_INIT_FAILED(err)) { - _PyPathConfig_Clear(&new_path_config); goto done; } - _Py_path_config = new_path_config; err = _Py_INIT_OK(); done: diff --git a/Modules/main.c b/Modules/main.c index 6c6c8018fd4..84706e1e290 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -876,6 +876,16 @@ static _PyInitError config_get_program_name(_PyMainInterpreterConfig *config) { assert(config->program_name == NULL); + + /* If Py_SetProgramName() was called, use its value */ + wchar_t *program_name = _Py_path_config.program_name; + if (program_name != NULL) { + config->program_name = _PyMem_RawWcsdup(program_name); + if (config->program_name == NULL) { + return _Py_INIT_NO_MEMORY(); + } + } + #ifdef __APPLE__ char *p; /* On MacOS X, when the Python interpreter is embedded in an @@ -914,6 +924,7 @@ config_get_program_name(_PyMainInterpreterConfig *config) } #endif /* WITH_NEXT_FRAMEWORK */ #endif /* __APPLE__ */ + return _Py_INIT_OK(); } @@ -948,13 +959,6 @@ pymain_init_main_interpreter(_PyMain *pymain) { _PyInitError err; - /* TODO: Print any exceptions raised by these operations */ - err = _PyMainInterpreterConfig_Read(&pymain->config); - if (_Py_INIT_FAILED(err)) { - pymain->err = err; - return -1; - } - err = _Py_InitializeMainInterpreter(&pymain->config); if (_Py_INIT_FAILED(err)) { pymain->err = err; @@ -1412,14 +1416,13 @@ config_init_pythonpath(_PyMainInterpreterConfig *config) static _PyInitError -config_init_pythonhome(_PyMainInterpreterConfig *config) +config_init_home(_PyMainInterpreterConfig *config) { wchar_t *home; - home = Py_GetPythonHome(); + /* If Py_SetPythonHome() was called, use its value */ + home = _Py_path_config.home; if (home) { - /* Py_SetPythonHome() has been called before Py_Main(), - use its value */ config->home = _PyMem_RawWcsdup(home); if (config->home == NULL) { return _Py_INIT_NO_MEMORY(); @@ -1439,7 +1442,7 @@ config_init_pythonhome(_PyMainInterpreterConfig *config) _PyInitError _PyMainInterpreterConfig_ReadEnv(_PyMainInterpreterConfig *config) { - _PyInitError err = config_init_pythonhome(config); + _PyInitError err = config_init_home(config); if (_Py_INIT_FAILED(err)) { return err; } @@ -1543,6 +1546,12 @@ pymain_parse_cmdline_envvars_impl(_PyMain *pymain) return -1; } + _PyInitError err = _PyMainInterpreterConfig_Read(&pymain->config); + if (_Py_INIT_FAILED(err)) { + pymain->err = err; + return -1; + } + return 0; } @@ -1566,11 +1575,6 @@ pymain_init_python(_PyMain *pymain) { pymain_init_stdio(pymain); - Py_SetProgramName(pymain->config.program_name); - /* Don't free program_name here: the argument to Py_SetProgramName - must remain valid until Py_FinalizeEx is called. The string is freed - by pymain_free(). */ - pymain->err = _Py_InitializeCore(&pymain->core_config); if (_Py_INIT_FAILED(pymain->err)) { return -1; diff --git a/PC/getpathp.c b/PC/getpathp.c index 3a0ebc10636..08ed8ccc83f 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -1058,38 +1058,23 @@ calculate_free(PyCalculatePath *calculate) } -/* Initialize paths for Py_GetPath(), Py_GetPrefix(), Py_GetExecPrefix() - and Py_GetProgramFullPath() */ _PyInitError -_PyPathConfig_Init(const _PyMainInterpreterConfig *main_config) +_PyPathConfig_Calculate(_PyPathConfig *config, + const _PyMainInterpreterConfig *main_config) { - if (_Py_path_config.module_search_path) { - /* Already initialized */ - return _Py_INIT_OK(); - } - - _PyInitError err; - PyCalculatePath calculate; memset(&calculate, 0, sizeof(calculate)); calculate_init(&calculate, main_config); - _PyPathConfig new_path_config; - memset(&new_path_config, 0, sizeof(new_path_config)); - - err = calculate_path_impl(main_config, &calculate, &new_path_config); + _PyInitError err = calculate_path_impl(main_config, &calculate, config); if (_Py_INIT_FAILED(err)) { goto done; } - _Py_path_config = new_path_config; err = _Py_INIT_OK(); done: - if (_Py_INIT_FAILED(err)) { - _PyPathConfig_Clear(&new_path_config); - } calculate_free(&calculate); return err; } diff --git a/Python/pathconfig.c b/Python/pathconfig.c index daa22270369..6a03f7dca1b 100644 --- a/Python/pathconfig.c +++ b/Python/pathconfig.c @@ -10,17 +10,17 @@ extern "C" { _PyPathConfig _Py_path_config = _PyPathConfig_INIT; -#ifdef MS_WINDOWS -static wchar_t *progname = L"python"; -#else -static wchar_t *progname = L"python3"; -#endif -static wchar_t *default_home = NULL; void _PyPathConfig_Clear(_PyPathConfig *config) { + /* _PyMem_SetDefaultAllocator() is needed to get a known memory allocator, + since Py_SetPath(), Py_SetPythonHome() and Py_SetProgramName() can be + called before Py_Initialize() which can changes the memory allocator. */ + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + #define CLEAR(ATTR) \ do { \ PyMem_RawFree(ATTR); \ @@ -35,57 +35,64 @@ _PyPathConfig_Clear(_PyPathConfig *config) CLEAR(config->exec_prefix); #endif CLEAR(config->module_search_path); + CLEAR(config->home); + CLEAR(config->program_name); #undef CLEAR + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); } -void -Py_SetProgramName(wchar_t *pn) +/* Initialize paths for Py_GetPath(), Py_GetPrefix(), Py_GetExecPrefix() + and Py_GetProgramFullPath() */ +_PyInitError +_PyPathConfig_Init(const _PyMainInterpreterConfig *main_config) { - if (pn && *pn) - progname = pn; -} - - -wchar_t * -Py_GetProgramName(void) -{ - return progname; -} - - -void -Py_SetPythonHome(wchar_t *home) -{ - default_home = home; -} - - -wchar_t* -Py_GetPythonHome(void) -{ - /* Use a static buffer to avoid heap memory allocation failure. - Py_GetPythonHome() doesn't allow to report error, and the caller - doesn't release memory. */ - static wchar_t buffer[MAXPATHLEN+1]; - - if (default_home) { - return default_home; + if (_Py_path_config.module_search_path) { + /* Already initialized */ + return _Py_INIT_OK(); } - char *home = Py_GETENV("PYTHONHOME"); - if (!home) { - return NULL; + _PyInitError err; + _PyPathConfig new_config = _PyPathConfig_INIT; + + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + /* Calculate program_full_path, prefix, exec_prefix (Unix) + or dll_path (Windows), and module_search_path */ + err = _PyPathConfig_Calculate(&new_config, main_config); + if (_Py_INIT_FAILED(err)) { + _PyPathConfig_Clear(&new_config); + goto done; } - size_t size = Py_ARRAY_LENGTH(buffer); - size_t r = mbstowcs(buffer, home, size); - if (r == (size_t)-1 || r >= size) { - /* conversion failed or the static buffer is too small */ - return NULL; + /* Copy home and program_name from main_config */ + if (main_config->home != NULL) { + new_config.home = _PyMem_RawWcsdup(main_config->home); + if (new_config.home == NULL) { + err = _Py_INIT_NO_MEMORY(); + goto done; + } + } + else { + new_config.home = NULL; } - return buffer; + new_config.program_name = _PyMem_RawWcsdup(main_config->program_name); + if (new_config.program_name == NULL) { + err = _Py_INIT_NO_MEMORY(); + goto done; + } + + _PyPathConfig_Clear(&_Py_path_config); + _Py_path_config = new_config; + + err = _Py_INIT_OK(); + +done: + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + return err; } @@ -134,6 +141,9 @@ Py_SetPath(const wchar_t *path) return; } + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + _PyPathConfig new_config; new_config.program_full_path = _PyMem_RawWcsdup(Py_GetProgramName()); new_config.prefix = _PyMem_RawWcsdup(L""); @@ -144,8 +154,58 @@ Py_SetPath(const wchar_t *path) #endif new_config.module_search_path = _PyMem_RawWcsdup(path); + /* steal the home and program_name values (to leave them unchanged) */ + new_config.home = _Py_path_config.home; + _Py_path_config.home = NULL; + new_config.program_name = _Py_path_config.program_name; + _Py_path_config.program_name = NULL; + _PyPathConfig_Clear(&_Py_path_config); _Py_path_config = new_config; + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); +} + + +void +Py_SetPythonHome(wchar_t *home) +{ + if (home == NULL) { + return; + } + + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + PyMem_RawFree(_Py_path_config.home); + _Py_path_config.home = _PyMem_RawWcsdup(home); + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + if (_Py_path_config.home == NULL) { + Py_FatalError("Py_SetPythonHome() failed: out of memory"); + } +} + + +void +Py_SetProgramName(wchar_t *program_name) +{ + if (program_name == NULL || program_name[0] == L'\0') { + return; + } + + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + PyMem_RawFree(_Py_path_config.program_name); + _Py_path_config.program_name = _PyMem_RawWcsdup(program_name); + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + if (_Py_path_config.program_name == NULL) { + Py_FatalError("Py_SetProgramName() failed: out of memory"); + } } @@ -184,6 +244,23 @@ Py_GetProgramFullPath(void) return _Py_path_config.program_full_path; } + +wchar_t* +Py_GetPythonHome(void) +{ + pathconfig_global_init(); + return _Py_path_config.home; +} + + +wchar_t * +Py_GetProgramName(void) +{ + pathconfig_global_init(); + return _Py_path_config.program_name; +} + + #ifdef __cplusplus } #endif diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 8d71154c563..523397f1269 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -804,7 +804,12 @@ _PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *config) } if (config->program_name == NULL) { - config->program_name = _PyMem_RawWcsdup(Py_GetProgramName()); +#ifdef MS_WINDOWS + const wchar_t *program_name = L"python"; +#else + const wchar_t *program_name = L"python3"; +#endif + config->program_name = _PyMem_RawWcsdup(program_name); if (config->program_name == NULL) { return _Py_INIT_NO_MEMORY(); }