From 3f5409a3f13c59baa34656bccefdc3728e46c9ef Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 23 Sep 2019 19:50:27 +0200 Subject: [PATCH] bpo-38234: Fix _PyConfig_InitPathConfig() (GH-16335) (GH-16336) * _PyConfig_InitPathConfig() now starts by copying the global path configuration, and then override values set in PyConfig. * _PyPathConfig_Calculate() implementations no longer override _PyPathConfig fields which are already computed. For example, if _PyPathConfig.prefix is not NULL, leave it unchanged. * If Py_SetPath() has been called, _PyConfig_InitPathConfig() doesn't call _PyPathConfig_Calculate() anymore. * _PyPathConfig_Calculate() no longer uses PyConfig, except to initialize PyCalculatePath structure. * pathconfig_calculate(): remove useless temporary "_PyPathConfig new_config" variable. * calculate_module_search_path(): remove hack to workaround memory allocation failure, call Py_FatalError() instead. * Fix get_program_full_path(): handle memory allocation failure. (cherry picked from commit 9c42f8cda552694f3b47d6388d4ae84d61731872) --- .../2019-09-20-17-22-41.bpo-38234.ZbquVK.rst | 5 +- Modules/getpath.c | 142 ++++---- PC/getpathp.c | 110 +++--- Python/pathconfig.c | 317 +++++++++--------- 4 files changed, 318 insertions(+), 256 deletions(-) diff --git a/Misc/NEWS.d/next/C API/2019-09-20-17-22-41.bpo-38234.ZbquVK.rst b/Misc/NEWS.d/next/C API/2019-09-20-17-22-41.bpo-38234.ZbquVK.rst index 6310f19f004..fe658ad75d8 100644 --- a/Misc/NEWS.d/next/C API/2019-09-20-17-22-41.bpo-38234.ZbquVK.rst +++ b/Misc/NEWS.d/next/C API/2019-09-20-17-22-41.bpo-38234.ZbquVK.rst @@ -1,2 +1,3 @@ -Python ignored path passed to :c:func:`Py_SetPath`, fix Python -initialization to use the specified path. +Python ignored arguments passed to :c:func:`Py_SetPath`, +:c:func:`Py_SetPythonHome` and :c:func:`Py_SetProgramName`: fix Python +initialization to use specified arguments. diff --git a/Modules/getpath.c b/Modules/getpath.c index 5afb5b15263..f60fe3c015b 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -133,6 +133,9 @@ typedef struct { int prefix_found; /* found platform independent libraries? */ int exec_prefix_found; /* found the platform dependent libraries? */ + + int warnings; + const wchar_t *pythonpath_env; } PyCalculatePath; static const wchar_t delimiter[2] = {DELIM, '\0'}; @@ -365,17 +368,16 @@ add_exe_suffix(wchar_t *progpath, size_t progpathlen) bytes long. */ static PyStatus -search_for_prefix(const PyConfig *config, PyCalculatePath *calculate, - wchar_t *prefix, size_t prefix_len, - int *found) +search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, + wchar_t *prefix, size_t prefix_len, int *found) { PyStatus status; size_t n; wchar_t *vpath; /* If PYTHONHOME is set, we believe it unconditionally */ - if (config->home) { - if (safe_wcscpy(prefix, config->home, prefix_len) < 0) { + if (pathconfig->home) { + if (safe_wcscpy(prefix, pathconfig->home, prefix_len) < 0) { return PATHLEN_ERR(); } wchar_t *delim = wcschr(prefix, DELIM); @@ -482,19 +484,19 @@ search_for_prefix(const PyConfig *config, PyCalculatePath *calculate, static PyStatus -calculate_prefix(const PyConfig *config, - PyCalculatePath *calculate, wchar_t *prefix, size_t prefix_len) +calculate_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, + wchar_t *prefix, size_t prefix_len) { PyStatus status; - status = search_for_prefix(config, calculate, prefix, prefix_len, - &calculate->prefix_found); + status = search_for_prefix(calculate, pathconfig, prefix, prefix_len, + &calculate->prefix_found); if (_PyStatus_EXCEPTION(status)) { return status; } if (!calculate->prefix_found) { - if (config->pathconfig_warnings) { + if (calculate->warnings) { fprintf(stderr, "Could not find platform independent libraries \n"); } @@ -544,8 +546,7 @@ calculate_reduce_prefix(PyCalculatePath *calculate, MAXPATHLEN bytes long. */ static PyStatus -search_for_exec_prefix(const PyConfig *config, - PyCalculatePath *calculate, +search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, wchar_t *exec_prefix, size_t exec_prefix_len, int *found) { @@ -553,15 +554,15 @@ search_for_exec_prefix(const PyConfig *config, size_t n; /* If PYTHONHOME is set, we believe it unconditionally */ - if (config->home) { - wchar_t *delim = wcschr(config->home, DELIM); + if (pathconfig->home) { + wchar_t *delim = wcschr(pathconfig->home, DELIM); if (delim) { if (safe_wcscpy(exec_prefix, delim+1, exec_prefix_len) < 0) { return PATHLEN_ERR(); } } else { - if (safe_wcscpy(exec_prefix, config->home, exec_prefix_len) < 0) { + if (safe_wcscpy(exec_prefix, pathconfig->home, exec_prefix_len) < 0) { return PATHLEN_ERR(); } } @@ -668,21 +669,20 @@ search_for_exec_prefix(const PyConfig *config, static PyStatus -calculate_exec_prefix(const PyConfig *config, - PyCalculatePath *calculate, +calculate_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, wchar_t *exec_prefix, size_t exec_prefix_len) { PyStatus status; - status = search_for_exec_prefix(config, calculate, - exec_prefix, exec_prefix_len, - &calculate->exec_prefix_found); + status = search_for_exec_prefix(calculate, pathconfig, + exec_prefix, exec_prefix_len, + &calculate->exec_prefix_found); if (_PyStatus_EXCEPTION(status)) { return status; } if (!calculate->exec_prefix_found) { - if (config->pathconfig_warnings) { + if (calculate->warnings) { fprintf(stderr, "Could not find platform dependent libraries \n"); } @@ -721,8 +721,7 @@ calculate_reduce_exec_prefix(PyCalculatePath *calculate, static PyStatus -calculate_program_full_path(const PyConfig *config, - PyCalculatePath *calculate, _PyPathConfig *pathconfig) +calculate_program_full_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig) { PyStatus status; wchar_t program_full_path[MAXPATHLEN + 1]; @@ -743,8 +742,8 @@ calculate_program_full_path(const PyConfig *config, * other way to find a directory to start the search from. If * $PATH isn't exported, you lose. */ - if (wcschr(config->program_name, SEP)) { - if (safe_wcscpy(program_full_path, config->program_name, + if (wcschr(pathconfig->program_name, SEP)) { + if (safe_wcscpy(program_full_path, pathconfig->program_name, program_full_path_len) < 0) { return PATHLEN_ERR(); } @@ -795,8 +794,8 @@ calculate_program_full_path(const PyConfig *config, } } - status = joinpath(program_full_path, config->program_name, - program_full_path_len); + status = joinpath(program_full_path, pathconfig->program_name, + program_full_path_len); if (_PyStatus_EXCEPTION(status)) { return status; } @@ -1030,15 +1029,14 @@ calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix) static PyStatus -calculate_module_search_path(const PyConfig *config, - PyCalculatePath *calculate, - const wchar_t *prefix, const wchar_t *exec_prefix, - _PyPathConfig *pathconfig) +calculate_module_search_path(PyCalculatePath *calculate, + _PyPathConfig *pathconfig, + const wchar_t *prefix, const wchar_t *exec_prefix) { /* Calculate size of return buffer */ size_t bufsz = 0; - if (config->pythonpath_env != NULL) { - bufsz += wcslen(config->pythonpath_env) + 1; + if (calculate->pythonpath_env != NULL) { + bufsz += wcslen(calculate->pythonpath_env) + 1; } wchar_t *defpath = calculate->pythonpath; @@ -1072,8 +1070,8 @@ calculate_module_search_path(const PyConfig *config, buf[0] = '\0'; /* Run-time value of $PYTHONPATH goes first */ - if (config->pythonpath_env) { - wcscpy(buf, config->pythonpath_env); + if (calculate->pythonpath_env) { + wcscpy(buf, calculate->pythonpath_env); wcscat(buf, delimiter); } @@ -1149,6 +1147,10 @@ calculate_init(PyCalculatePath *calculate, if (!calculate->lib_python) { return DECODE_LOCALE_ERR("EXEC_PREFIX define", len); } + + calculate->warnings = config->pathconfig_warnings; + calculate->pythonpath_env = config->pythonpath_env; + return _PyStatus_OK(); } @@ -1165,14 +1167,15 @@ calculate_free(PyCalculatePath *calculate) static PyStatus -calculate_path_impl(const PyConfig *config, - PyCalculatePath *calculate, _PyPathConfig *pathconfig) +calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig) { PyStatus status; - status = calculate_program_full_path(config, calculate, pathconfig); - if (_PyStatus_EXCEPTION(status)) { - return status; + if (pathconfig->program_full_path == NULL) { + status = calculate_program_full_path(calculate, pathconfig); + if (_PyStatus_EXCEPTION(status)) { + return status; + } } status = calculate_argv0_path(calculate, pathconfig->program_full_path); @@ -1187,8 +1190,8 @@ calculate_path_impl(const PyConfig *config, wchar_t prefix[MAXPATHLEN+1]; memset(prefix, 0, sizeof(prefix)); - status = calculate_prefix(config, calculate, - prefix, Py_ARRAY_LENGTH(prefix)); + status = calculate_prefix(calculate, pathconfig, + prefix, Py_ARRAY_LENGTH(prefix)); if (_PyStatus_EXCEPTION(status)) { return status; } @@ -1200,52 +1203,65 @@ calculate_path_impl(const PyConfig *config, wchar_t exec_prefix[MAXPATHLEN+1]; memset(exec_prefix, 0, sizeof(exec_prefix)); - status = calculate_exec_prefix(config, calculate, - exec_prefix, Py_ARRAY_LENGTH(exec_prefix)); + status = calculate_exec_prefix(calculate, pathconfig, + exec_prefix, Py_ARRAY_LENGTH(exec_prefix)); if (_PyStatus_EXCEPTION(status)) { return status; } if ((!calculate->prefix_found || !calculate->exec_prefix_found) && - config->pathconfig_warnings) + calculate->warnings) { fprintf(stderr, "Consider setting $PYTHONHOME to [:]\n"); } if (pathconfig->module_search_path == NULL) { - status = calculate_module_search_path(config, calculate, - prefix, exec_prefix, pathconfig); + status = calculate_module_search_path(calculate, pathconfig, + prefix, exec_prefix); if (_PyStatus_EXCEPTION(status)) { return status; } } - status = calculate_reduce_prefix(calculate, prefix, Py_ARRAY_LENGTH(prefix)); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - - pathconfig->prefix = _PyMem_RawWcsdup(prefix); if (pathconfig->prefix == NULL) { - return _PyStatus_NO_MEMORY(); + status = calculate_reduce_prefix(calculate, prefix, Py_ARRAY_LENGTH(prefix)); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + + pathconfig->prefix = _PyMem_RawWcsdup(prefix); + if (pathconfig->prefix == NULL) { + return _PyStatus_NO_MEMORY(); + } } - status = calculate_reduce_exec_prefix(calculate, - exec_prefix, Py_ARRAY_LENGTH(exec_prefix)); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - - pathconfig->exec_prefix = _PyMem_RawWcsdup(exec_prefix); if (pathconfig->exec_prefix == NULL) { - return _PyStatus_NO_MEMORY(); + status = calculate_reduce_exec_prefix(calculate, + exec_prefix, + Py_ARRAY_LENGTH(exec_prefix)); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + + pathconfig->exec_prefix = _PyMem_RawWcsdup(exec_prefix); + if (pathconfig->exec_prefix == NULL) { + return _PyStatus_NO_MEMORY(); + } } return _PyStatus_OK(); } +/* Calculate 'pathconfig' attributes: + + - program_full_path + - module_search_path + - prefix + - exec_prefix + + If an attribute is already set (non NULL), it is left unchanged. */ PyStatus _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config) { @@ -1258,7 +1274,7 @@ _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config) goto done; } - status = calculate_path_impl(config, &calculate, pathconfig); + status = calculate_path(&calculate, pathconfig); if (_PyStatus_EXCEPTION(status)) { goto done; } diff --git a/PC/getpathp.c b/PC/getpathp.c index 888bef1146f..5670fa2ee4e 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -130,6 +130,8 @@ typedef struct { wchar_t zip_path[MAXPATHLEN+1]; wchar_t *dll_path; + + const wchar_t *pythonpath_env; } PyCalculatePath; @@ -322,7 +324,7 @@ gotlandmark(wchar_t *prefix, const wchar_t *landmark) /* assumes argv0_path is MAXPATHLEN+1 bytes long, already \0 term'd. - assumption provided by only caller, calculate_path_impl() */ + assumption provided by only caller, calculate_path() */ static int search_for_prefix(wchar_t *prefix, const wchar_t *argv0_path, const wchar_t *landmark) { @@ -534,6 +536,7 @@ _Py_GetDLLPath(void) static PyStatus get_program_full_path(_PyPathConfig *pathconfig) { + PyStatus status; const wchar_t *pyvenv_launcher; wchar_t program_full_path[MAXPATHLEN+1]; memset(program_full_path, 0, sizeof(program_full_path)); @@ -548,12 +551,18 @@ get_program_full_path(_PyPathConfig *pathconfig) pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__"); if (pyvenv_launcher && pyvenv_launcher[0]) { /* If overridden, preserve the original full path */ - pathconfig->base_executable = PyMem_RawMalloc( - sizeof(wchar_t) * (MAXPATHLEN + 1)); - PyStatus status = canonicalize(pathconfig->base_executable, - program_full_path); - if (_PyStatus_EXCEPTION(status)) { - return status; + if (pathconfig->base_executable == NULL) { + pathconfig->base_executable = PyMem_RawMalloc( + sizeof(wchar_t) * (MAXPATHLEN + 1)); + if (pathconfig->base_executable == NULL) { + return _PyStatus_NO_MEMORY(); + } + + status = canonicalize(pathconfig->base_executable, + program_full_path); + if (_PyStatus_EXCEPTION(status)) { + return status; + } } wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher); @@ -562,11 +571,20 @@ get_program_full_path(_PyPathConfig *pathconfig) _wputenv_s(L"__PYVENV_LAUNCHER__", L""); } - pathconfig->program_full_path = PyMem_RawMalloc( - sizeof(wchar_t) * (MAXPATHLEN + 1)); + if (pathconfig->program_full_path == NULL) { + pathconfig->program_full_path = PyMem_RawMalloc( + sizeof(wchar_t) * (MAXPATHLEN + 1)); + if (pathconfig->program_full_path == NULL) { + return _PyStatus_NO_MEMORY(); + } - return canonicalize(pathconfig->program_full_path, - program_full_path); + status = canonicalize(pathconfig->program_full_path, + program_full_path); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + } + return _PyStatus_OK(); } @@ -657,7 +675,13 @@ read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path) } fclose(sp_file); - pathconfig->module_search_path = buf; + if (pathconfig->module_search_path == NULL) { + pathconfig->module_search_path = _PyMem_RawWcsdup(buf); + if (pathconfig->module_search_path == NULL) { + Py_FatalError("out of memory"); + } + } + PyMem_RawFree(buf); return 1; error: @@ -668,8 +692,7 @@ error: static PyStatus -calculate_init(PyCalculatePath *calculate, - const PyConfig *config) +calculate_init(PyCalculatePath *calculate, const PyConfig *config) { calculate->home = config->home; calculate->path_env = _wgetenv(L"PATH"); @@ -679,6 +702,8 @@ calculate_init(PyCalculatePath *calculate, return _PyStatus_NO_MEMORY(); } + calculate->pythonpath_env = config->pythonpath_env; + return _PyStatus_OK(); } @@ -785,8 +810,8 @@ calculate_home_prefix(PyCalculatePath *calculate, wchar_t *prefix) static PyStatus -calculate_module_search_path(const PyConfig *config, - PyCalculatePath *calculate, _PyPathConfig *pathconfig, +calculate_module_search_path(PyCalculatePath *calculate, + _PyPathConfig *pathconfig, wchar_t *prefix) { int skiphome = calculate->home==NULL ? 0 : 1; @@ -796,7 +821,7 @@ calculate_module_search_path(const PyConfig *config, #endif /* We only use the default relative PYTHONPATH if we haven't anything better to use! */ - int skipdefault = (config->pythonpath_env != NULL || + int skipdefault = (calculate->pythonpath_env != NULL || calculate->home != NULL || calculate->machine_path != NULL || calculate->user_path != NULL); @@ -835,30 +860,20 @@ calculate_module_search_path(const PyConfig *config, bufsz += wcslen(calculate->machine_path) + 1; } bufsz += wcslen(calculate->zip_path) + 1; - if (config->pythonpath_env != NULL) { - bufsz += wcslen(config->pythonpath_env) + 1; + if (calculate->pythonpath_env != NULL) { + bufsz += wcslen(calculate->pythonpath_env) + 1; } wchar_t *buf, *start_buf; buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t)); if (buf == NULL) { - /* We can't exit, so print a warning and limp along */ - fprintf(stderr, "Can't malloc dynamic PYTHONPATH.\n"); - if (config->pythonpath_env) { - fprintf(stderr, "Using environment $PYTHONPATH.\n"); - pathconfig->module_search_path = config->pythonpath_env; - } - else { - fprintf(stderr, "Using default static path.\n"); - pathconfig->module_search_path = PYTHONPATH; - } - return _PyStatus_OK(); + Py_FatalError("Can't malloc dynamic PYTHONPATH"); } start_buf = buf; - if (config->pythonpath_env) { + if (calculate->pythonpath_env) { if (wcscpy_s(buf, bufsz - (buf - start_buf), - config->pythonpath_env)) { + calculate->pythonpath_env)) { return INIT_ERR_BUFFER_OVERFLOW(); } buf = wcschr(buf, L'\0'); @@ -971,8 +986,7 @@ calculate_module_search_path(const PyConfig *config, static PyStatus -calculate_path_impl(const PyConfig *config, - PyCalculatePath *calculate, _PyPathConfig *pathconfig) +calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig) { PyStatus status; @@ -1003,21 +1017,24 @@ calculate_path_impl(const PyConfig *config, calculate_home_prefix(calculate, prefix); if (pathconfig->module_search_path == NULL) { - status = calculate_module_search_path(config, calculate, - pathconfig, prefix); + status = calculate_module_search_path(calculate, pathconfig, prefix); if (_PyStatus_EXCEPTION(status)) { return status; } } done: - pathconfig->prefix = _PyMem_RawWcsdup(prefix); if (pathconfig->prefix == NULL) { - return _PyStatus_NO_MEMORY(); + pathconfig->prefix = _PyMem_RawWcsdup(prefix); + if (pathconfig->prefix == NULL) { + return _PyStatus_NO_MEMORY(); + } } - pathconfig->exec_prefix = _PyMem_RawWcsdup(prefix); if (pathconfig->exec_prefix == NULL) { - return _PyStatus_NO_MEMORY(); + pathconfig->exec_prefix = _PyMem_RawWcsdup(prefix); + if (pathconfig->exec_prefix == NULL) { + return _PyStatus_NO_MEMORY(); + } } return _PyStatus_OK(); @@ -1033,6 +1050,17 @@ calculate_free(PyCalculatePath *calculate) } +/* Calculate 'pathconfig' attributes: + + - base_executable + - program_full_path + - module_search_path + - prefix + - exec_prefix + - isolated + - site_import + + If an attribute is already set (non NULL), it is left unchanged. */ PyStatus _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config) { @@ -1045,7 +1073,7 @@ _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config) goto done; } - status = calculate_path_impl(config, &calculate, pathconfig); + status = calculate_path(&calculate, pathconfig); done: calculate_free(&calculate); diff --git a/Python/pathconfig.c b/Python/pathconfig.c index 1934f2e1c4b..09533100b46 100644 --- a/Python/pathconfig.c +++ b/Python/pathconfig.c @@ -51,12 +51,12 @@ pathconfig_clear(_PyPathConfig *config) ATTR = NULL; \ } while (0) - CLEAR(config->prefix); CLEAR(config->program_full_path); + CLEAR(config->prefix); CLEAR(config->exec_prefix); CLEAR(config->module_search_path); - CLEAR(config->home); CLEAR(config->program_name); + CLEAR(config->home); CLEAR(config->base_executable); #undef CLEAR @@ -64,60 +64,31 @@ pathconfig_clear(_PyPathConfig *config) } -/* Calculate the path configuration: initialize pathconfig from config */ static PyStatus -pathconfig_calculate(_PyPathConfig *pathconfig, const PyConfig *config) +pathconfig_copy(_PyPathConfig *config, const _PyPathConfig *config2) { - PyStatus status; - _PyPathConfig new_config = _PyPathConfig_INIT; + pathconfig_clear(config); - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); +#define COPY_ATTR(ATTR) \ + do { \ + if (copy_wstr(&config->ATTR, config2->ATTR) < 0) { \ + return _PyStatus_NO_MEMORY(); \ + } \ + } while (0) - if (copy_wstr(&new_config.module_search_path, - _Py_path_config.module_search_path) < 0) - { - status = _PyStatus_NO_MEMORY(); - goto error; - } + COPY_ATTR(program_full_path); + COPY_ATTR(prefix); + COPY_ATTR(exec_prefix); + COPY_ATTR(module_search_path); + COPY_ATTR(program_name); + COPY_ATTR(home); + config->isolated = config2->isolated; + config->site_import = config2->site_import; + COPY_ATTR(base_executable); - /* Calculate program_full_path, prefix, exec_prefix, - dll_path (Windows), and module_search_path */ - status = _PyPathConfig_Calculate(&new_config, config); - if (_PyStatus_EXCEPTION(status)) { - goto error; - } +#undef COPY_ATTR - /* Copy home and program_name from config */ - if (copy_wstr(&new_config.home, config->home) < 0) { - status = _PyStatus_NO_MEMORY(); - goto error; - } - if (copy_wstr(&new_config.program_name, config->program_name) < 0) { - status = _PyStatus_NO_MEMORY(); - goto error; - } - if (config->base_executable) { - PyMem_RawFree(new_config.base_executable); - if (copy_wstr(&new_config.base_executable, - config->base_executable) < 0) { - status = _PyStatus_NO_MEMORY(); - goto error; - } - } - - pathconfig_clear(pathconfig); - *pathconfig = new_config; - - status = _PyStatus_OK(); - goto done; - -error: - pathconfig_clear(&new_config); - -done: - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - return status; + return _PyStatus_OK(); } @@ -168,7 +139,7 @@ _PyWideStringList_Join(const PyWideStringList *list, wchar_t sep) } -/* Set the global path configuration from config. */ +/* Initialize _Py_dll_path on Windows. Do nothing on other platforms. */ PyStatus _PyPathConfig_Init(void) { @@ -194,47 +165,39 @@ _PyPathConfig_Init(void) static PyStatus -pathconfig_global_init_from_config(const PyConfig *config) +pathconfig_set_from_config(_PyPathConfig *pathconfig, const PyConfig *config) { PyStatus status; PyMemAllocatorEx old_alloc; _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - _PyPathConfig pathconfig = _PyPathConfig_INIT; - - pathconfig.module_search_path = _PyWideStringList_Join(&config->module_search_paths, DELIM); - if (pathconfig.module_search_path == NULL) { - goto no_memory; + if (config->module_search_paths_set) { + pathconfig->module_search_path = _PyWideStringList_Join(&config->module_search_paths, DELIM); + if (pathconfig->module_search_path == NULL) { + goto no_memory; + } } - if (copy_wstr(&pathconfig.program_full_path, config->executable) < 0) { - goto no_memory; - } - if (copy_wstr(&pathconfig.prefix, config->prefix) < 0) { - goto no_memory; - } - if (copy_wstr(&pathconfig.exec_prefix, config->exec_prefix) < 0) { - goto no_memory; - } - if (copy_wstr(&pathconfig.program_name, config->program_name) < 0) { - goto no_memory; - } - if (copy_wstr(&pathconfig.home, config->home) < 0) { - goto no_memory; - } - if (copy_wstr(&pathconfig.base_executable, config->base_executable) < 0) { - goto no_memory; - } +#define COPY_CONFIG(PATH_ATTR, CONFIG_ATTR) \ + if (config->CONFIG_ATTR) { \ + if (copy_wstr(&pathconfig->PATH_ATTR, config->CONFIG_ATTR) < 0) { \ + goto no_memory; \ + } \ + } - pathconfig_clear(&_Py_path_config); - /* Steal new_config strings; don't clear new_config */ - _Py_path_config = pathconfig; + COPY_CONFIG(base_executable, base_executable); + COPY_CONFIG(program_full_path, executable); + COPY_CONFIG(prefix, prefix); + COPY_CONFIG(exec_prefix, exec_prefix); + COPY_CONFIG(program_name, program_name); + COPY_CONFIG(home, home); + +#undef COPY_CONFIG status = _PyStatus_OK(); goto done; no_memory: - pathconfig_clear(&pathconfig); status = _PyStatus_NO_MEMORY(); done: @@ -283,6 +246,61 @@ config_init_module_search_paths(PyConfig *config, _PyPathConfig *pathconfig) } +/* Calculate the path configuration: + + - exec_prefix + - module_search_path + - prefix + - program_full_path + + On Windows, more fields are calculated: + + - base_executable + - isolated + - site_import + + On other platforms, isolated and site_import are left unchanged, and + _PyConfig_InitPathConfig() copies executable to base_executable (if it's not + set). + + Priority, highest to lowest: + + - PyConfig + - _Py_path_config: set by Py_SetPath(), Py_SetPythonHome() + and Py_SetProgramName() + - _PyPathConfig_Calculate() +*/ +static PyStatus +pathconfig_calculate(_PyPathConfig *pathconfig, const PyConfig *config) +{ + PyStatus status; + + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + status = pathconfig_copy(pathconfig, &_Py_path_config); + if (_PyStatus_EXCEPTION(status)) { + goto done; + } + + status = pathconfig_set_from_config(pathconfig, config); + if (_PyStatus_EXCEPTION(status)) { + goto done; + } + + if (_Py_path_config.module_search_path == NULL) { + status = _PyPathConfig_Calculate(pathconfig, config); + } + else { + /* Py_SetPath() has been called: avoid _PyPathConfig_Calculate() */ + } + +done: + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + return status; +} + + static PyStatus config_calculate_pathconfig(PyConfig *config) { @@ -291,42 +309,28 @@ config_calculate_pathconfig(PyConfig *config) status = pathconfig_calculate(&pathconfig, config); if (_PyStatus_EXCEPTION(status)) { - goto error; + goto done; } if (!config->module_search_paths_set) { status = config_init_module_search_paths(config, &pathconfig); if (_PyStatus_EXCEPTION(status)) { - goto error; + goto done; } } - if (config->executable == NULL) { - if (copy_wstr(&config->executable, - pathconfig.program_full_path) < 0) { - goto no_memory; +#define COPY_ATTR(PATH_ATTR, CONFIG_ATTR) \ + if (config->CONFIG_ATTR == NULL) { \ + if (copy_wstr(&config->CONFIG_ATTR, pathconfig.PATH_ATTR) < 0) { \ + goto no_memory; \ + } \ } - } - if (config->prefix == NULL) { - if (copy_wstr(&config->prefix, pathconfig.prefix) < 0) { - goto no_memory; - } - } - - if (config->exec_prefix == NULL) { - if (copy_wstr(&config->exec_prefix, - pathconfig.exec_prefix) < 0) { - goto no_memory; - } - } - - if (config->base_executable == NULL) { - if (copy_wstr(&config->base_executable, - pathconfig.base_executable) < 0) { - goto no_memory; - } - } + COPY_ATTR(program_full_path, executable); + COPY_ATTR(prefix, prefix); + COPY_ATTR(exec_prefix, exec_prefix); + COPY_ATTR(base_executable, base_executable); +#undef COPY_ATTR if (pathconfig.isolated != -1) { config->isolated = pathconfig.isolated; @@ -335,13 +339,13 @@ config_calculate_pathconfig(PyConfig *config) config->site_import = pathconfig.site_import; } - pathconfig_clear(&pathconfig); - return _PyStatus_OK(); + status = _PyStatus_OK(); + goto done; no_memory: status = _PyStatus_NO_MEMORY(); -error: +done: pathconfig_clear(&pathconfig); return status; } @@ -386,39 +390,55 @@ _PyConfig_InitPathConfig(PyConfig *config) } +static PyStatus +pathconfig_global_read(_PyPathConfig *pathconfig) +{ + PyStatus status; + PyConfig config; + _PyConfig_InitCompatConfig(&config); + + /* Call _PyConfig_InitPathConfig() */ + status = PyConfig_Read(&config); + if (_PyStatus_EXCEPTION(status)) { + goto done; + } + + status = pathconfig_set_from_config(pathconfig, &config); + +done: + PyConfig_Clear(&config); + return status; +} + + static void pathconfig_global_init(void) { + PyStatus status; + /* Initialize _Py_dll_path if needed */ - PyStatus status = _PyPathConfig_Init(); + status = _PyPathConfig_Init(); if (_PyStatus_EXCEPTION(status)) { Py_ExitStatusException(status); } - if (_Py_path_config.module_search_path != NULL) { - /* Already initialized */ - return; + if (_Py_path_config.module_search_path == NULL) { + status = pathconfig_global_read(&_Py_path_config); + if (_PyStatus_EXCEPTION(status)) { + Py_ExitStatusException(status); + } + } + else { + /* Global configuration already initialized */ } - PyConfig config; - _PyConfig_InitCompatConfig(&config); - - status = PyConfig_Read(&config); - if (_PyStatus_EXCEPTION(status)) { - goto error; - } - - status = pathconfig_global_init_from_config(&config); - if (_PyStatus_EXCEPTION(status)) { - goto error; - } - - PyConfig_Clear(&config); - return; - -error: - PyConfig_Clear(&config); - Py_ExitStatusException(status); + assert(_Py_path_config.program_full_path != NULL); + assert(_Py_path_config.prefix != NULL); + assert(_Py_path_config.exec_prefix != NULL); + assert(_Py_path_config.module_search_path != NULL); + assert(_Py_path_config.program_name != NULL); + /* home can be NULL */ + assert(_Py_path_config.base_executable != NULL); } @@ -435,30 +455,27 @@ Py_SetPath(const wchar_t *path) PyMemAllocatorEx old_alloc; _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - _PyPathConfig new_config; - new_config.program_full_path = _PyMem_RawWcsdup(Py_GetProgramName()); - int alloc_error = (new_config.program_full_path == NULL); - new_config.prefix = _PyMem_RawWcsdup(L""); - alloc_error |= (new_config.prefix == NULL); - new_config.exec_prefix = _PyMem_RawWcsdup(L""); - alloc_error |= (new_config.exec_prefix == NULL); - new_config.module_search_path = _PyMem_RawWcsdup(path); - alloc_error |= (new_config.module_search_path == NULL); + /* Getting the program name calls pathconfig_global_init() */ + wchar_t *program_name = _PyMem_RawWcsdup(Py_GetProgramName()); - /* 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; - new_config.base_executable = _Py_path_config.base_executable; - _Py_path_config.base_executable = NULL; + PyMem_RawFree(_Py_path_config.program_full_path); + PyMem_RawFree(_Py_path_config.prefix); + PyMem_RawFree(_Py_path_config.exec_prefix); + PyMem_RawFree(_Py_path_config.module_search_path); - pathconfig_clear(&_Py_path_config); - _Py_path_config = new_config; + /* Copy program_name to program_full_path */ + _Py_path_config.program_full_path = program_name; + _Py_path_config.prefix = _PyMem_RawWcsdup(L""); + _Py_path_config.exec_prefix = _PyMem_RawWcsdup(L""); + _Py_path_config.module_search_path = _PyMem_RawWcsdup(path); PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - if (alloc_error) { + if (_Py_path_config.program_full_path == NULL + || _Py_path_config.prefix == NULL + || _Py_path_config.exec_prefix == NULL + || _Py_path_config.module_search_path == NULL) + { Py_FatalError("Py_SetPath() failed: out of memory"); } }