bpo-38234: read_pth_file() now returns PyStatus (GH-16338)

Refactor path configuration code:

* read_pth_file() now returns PyStatus to report errors, rather than
  calling Py_FatalError().
* Move argv0_path and zip_path buffers out of PyCalculatePath
  structures.
* On Windows, _PyPathConfig.home is now preferred over PyConfig.home.
This commit is contained in:
Victor Stinner 2019-09-24 00:55:48 +02:00 committed by GitHub
parent 9c42f8cda5
commit 85ce0a7178
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 243 additions and 155 deletions

View file

@ -123,13 +123,11 @@ extern "C" {
typedef struct { typedef struct {
wchar_t *path_env; /* PATH environment variable */ wchar_t *path_env; /* PATH environment variable */
wchar_t *pythonpath; /* PYTHONPATH define */ wchar_t *pythonpath; /* PYTHONPATH macro */
wchar_t *prefix; /* PREFIX define */ wchar_t *prefix; /* PREFIX macro */
wchar_t *exec_prefix; /* EXEC_PREFIX define */ wchar_t *exec_prefix; /* EXEC_PREFIX macro */
wchar_t *lib_python; /* "lib/pythonX.Y" */ wchar_t *lib_python; /* "lib/pythonX.Y" */
wchar_t argv0_path[MAXPATHLEN+1];
wchar_t zip_path[MAXPATHLEN+1]; /* ".../lib/pythonXY.zip" */
int prefix_found; /* found platform independent libraries? */ int prefix_found; /* found platform independent libraries? */
int exec_prefix_found; /* found the platform dependent libraries? */ int exec_prefix_found; /* found the platform dependent libraries? */
@ -369,6 +367,7 @@ add_exe_suffix(wchar_t *progpath, size_t progpathlen)
*/ */
static PyStatus static PyStatus
search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
const wchar_t *argv0_path,
wchar_t *prefix, size_t prefix_len, int *found) wchar_t *prefix, size_t prefix_len, int *found)
{ {
PyStatus status; PyStatus status;
@ -397,7 +396,7 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
} }
/* Check to see if argv[0] is in the build directory */ /* Check to see if argv[0] is in the build directory */
if (safe_wcscpy(prefix, calculate->argv0_path, prefix_len) < 0) { if (safe_wcscpy(prefix, argv0_path, prefix_len) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
status = joinpath(prefix, L"Modules/Setup.local", prefix_len); status = joinpath(prefix, L"Modules/Setup.local", prefix_len);
@ -409,7 +408,7 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
/* Check VPATH to see if argv0_path is in the build directory. */ /* Check VPATH to see if argv0_path is in the build directory. */
vpath = Py_DecodeLocale(VPATH, NULL); vpath = Py_DecodeLocale(VPATH, NULL);
if (vpath != NULL) { if (vpath != NULL) {
if (safe_wcscpy(prefix, calculate->argv0_path, prefix_len) < 0) { if (safe_wcscpy(prefix, argv0_path, prefix_len) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
status = joinpath(prefix, vpath, prefix_len); status = joinpath(prefix, vpath, prefix_len);
@ -435,7 +434,7 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
} }
/* Search from argv0_path, until root is found */ /* Search from argv0_path, until root is found */
status = copy_absolute(prefix, calculate->argv0_path, prefix_len); status = copy_absolute(prefix, argv0_path, prefix_len);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
@ -485,11 +484,13 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
static PyStatus static PyStatus
calculate_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, calculate_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
const wchar_t *argv0_path,
wchar_t *prefix, size_t prefix_len) wchar_t *prefix, size_t prefix_len)
{ {
PyStatus status; PyStatus status;
status = search_for_prefix(calculate, pathconfig, prefix, prefix_len, status = search_for_prefix(calculate, pathconfig, argv0_path,
prefix, prefix_len,
&calculate->prefix_found); &calculate->prefix_found);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
@ -516,8 +517,8 @@ calculate_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
static PyStatus static PyStatus
calculate_reduce_prefix(PyCalculatePath *calculate, calculate_set_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
wchar_t *prefix, size_t prefix_len) wchar_t *prefix)
{ {
/* Reduce prefix and exec_prefix to their essence, /* Reduce prefix and exec_prefix to their essence,
* e.g. /usr/local/lib/python1.5 is reduced to /usr/local. * e.g. /usr/local/lib/python1.5 is reduced to /usr/local.
@ -532,11 +533,14 @@ calculate_reduce_prefix(PyCalculatePath *calculate,
if (!prefix[0]) { if (!prefix[0]) {
wcscpy(prefix, separator); wcscpy(prefix, separator);
} }
pathconfig->prefix = _PyMem_RawWcsdup(prefix);
} }
else { else {
if (safe_wcscpy(prefix, calculate->prefix, prefix_len) < 0) { pathconfig->prefix = _PyMem_RawWcsdup(calculate->prefix);
return PATHLEN_ERR(); }
}
if (pathconfig->prefix == NULL) {
return _PyStatus_NO_MEMORY();
} }
return _PyStatus_OK(); return _PyStatus_OK();
} }
@ -547,6 +551,7 @@ calculate_reduce_prefix(PyCalculatePath *calculate,
*/ */
static PyStatus static PyStatus
search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
const wchar_t *argv0_path,
wchar_t *exec_prefix, size_t exec_prefix_len, wchar_t *exec_prefix, size_t exec_prefix_len,
int *found) int *found)
{ {
@ -581,7 +586,7 @@ search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
/* Check to see if argv[0] is in the build directory. "pybuilddir.txt" /* Check to see if argv[0] is in the build directory. "pybuilddir.txt"
is written by setup.py and contains the relative path to the location is written by setup.py and contains the relative path to the location
of shared library modules. */ of shared library modules. */
if (safe_wcscpy(exec_prefix, calculate->argv0_path, exec_prefix_len) < 0) { if (safe_wcscpy(exec_prefix, argv0_path, exec_prefix_len) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
status = joinpath(exec_prefix, L"pybuilddir.txt", exec_prefix_len); status = joinpath(exec_prefix, L"pybuilddir.txt", exec_prefix_len);
@ -607,7 +612,7 @@ search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
return DECODE_LOCALE_ERR("pybuilddir.txt", dec_len); return DECODE_LOCALE_ERR("pybuilddir.txt", dec_len);
} }
if (safe_wcscpy(exec_prefix, calculate->argv0_path, exec_prefix_len) < 0) { if (safe_wcscpy(exec_prefix, argv0_path, exec_prefix_len) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
status = joinpath(exec_prefix, pybuilddir, exec_prefix_len); status = joinpath(exec_prefix, pybuilddir, exec_prefix_len);
@ -622,7 +627,7 @@ search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
} }
/* Search from argv0_path, until root is found */ /* Search from argv0_path, until root is found */
status = copy_absolute(exec_prefix, calculate->argv0_path, exec_prefix_len); status = copy_absolute(exec_prefix, argv0_path, exec_prefix_len);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
@ -670,11 +675,12 @@ search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
static PyStatus static PyStatus
calculate_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, calculate_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
const wchar_t *argv0_path,
wchar_t *exec_prefix, size_t exec_prefix_len) wchar_t *exec_prefix, size_t exec_prefix_len)
{ {
PyStatus status; PyStatus status;
status = search_for_exec_prefix(calculate, pathconfig, status = search_for_exec_prefix(calculate, pathconfig, argv0_path,
exec_prefix, exec_prefix_len, exec_prefix, exec_prefix_len,
&calculate->exec_prefix_found); &calculate->exec_prefix_found);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
@ -700,8 +706,9 @@ calculate_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
static PyStatus static PyStatus
calculate_reduce_exec_prefix(PyCalculatePath *calculate, calculate_set_exec_prefix(PyCalculatePath *calculate,
wchar_t *exec_prefix, size_t exec_prefix_len) _PyPathConfig *pathconfig,
wchar_t *exec_prefix)
{ {
if (calculate->exec_prefix_found > 0) { if (calculate->exec_prefix_found > 0) {
reduce(exec_prefix); reduce(exec_prefix);
@ -710,12 +717,17 @@ calculate_reduce_exec_prefix(PyCalculatePath *calculate,
if (!exec_prefix[0]) { if (!exec_prefix[0]) {
wcscpy(exec_prefix, separator); wcscpy(exec_prefix, separator);
} }
pathconfig->exec_prefix = _PyMem_RawWcsdup(exec_prefix);
} }
else { else {
if (safe_wcscpy(exec_prefix, calculate->exec_prefix, exec_prefix_len) < 0) { pathconfig->exec_prefix = _PyMem_RawWcsdup(calculate->exec_prefix);
return PATHLEN_ERR();
}
} }
if (pathconfig->exec_prefix == NULL) {
return _PyStatus_NO_MEMORY();
}
return _PyStatus_OK(); return _PyStatus_OK();
} }
@ -843,10 +855,10 @@ calculate_program_full_path(PyCalculatePath *calculate, _PyPathConfig *pathconfi
static PyStatus static PyStatus
calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_path) calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_path,
wchar_t *argv0_path, size_t argv0_path_len)
{ {
const size_t argv0_path_len = Py_ARRAY_LENGTH(calculate->argv0_path); if (safe_wcscpy(argv0_path, program_full_path, argv0_path_len) < 0) {
if (safe_wcscpy(calculate->argv0_path, program_full_path, argv0_path_len) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
@ -877,32 +889,31 @@ calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_pat
return DECODE_LOCALE_ERR("framework location", len); return DECODE_LOCALE_ERR("framework location", len);
} }
if (safe_wcscpy(calculate->argv0_path, wbuf, argv0_path_len) < 0) { if (safe_wcscpy(argv0_path, wbuf, argv0_path_len) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
reduce(calculate->argv0_path); reduce(argv0_path);
status = joinpath(calculate->argv0_path, calculate->lib_python, argv0_path_len); status = joinpath(argv0_path, calculate->lib_python, argv0_path_len);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
PyMem_RawFree(wbuf); PyMem_RawFree(wbuf);
return status; return status;
} }
status = joinpath(calculate->argv0_path, LANDMARK, argv0_path_len); status = joinpath(argv0_path, LANDMARK, argv0_path_len);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
PyMem_RawFree(wbuf); PyMem_RawFree(wbuf);
return status; return status;
} }
if (!ismodule(calculate->argv0_path, if (!ismodule(argv0_path, Py_ARRAY_LENGTH(argv0_path))) {
Py_ARRAY_LENGTH(calculate->argv0_path))) {
/* We are in the build directory so use the name of the /* We are in the build directory so use the name of the
executable - we know that the absolute path is passed */ executable - we know that the absolute path is passed */
if (safe_wcscpy(calculate->argv0_path, program_full_path, if (safe_wcscpy(argv0_path, program_full_path,
argv0_path_len) < 0) { argv0_path_len) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
} }
else { else {
/* Use the location of the library as the program_full_path */ /* Use the location of the library as the program_full_path */
if (safe_wcscpy(calculate->argv0_path, wbuf, argv0_path_len) < 0) { if (safe_wcscpy(argv0_path, wbuf, argv0_path_len) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
} }
@ -918,24 +929,24 @@ calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_pat
if (_Py_isabs(tmpbuffer)) { if (_Py_isabs(tmpbuffer)) {
/* tmpbuffer should never be longer than MAXPATHLEN, /* tmpbuffer should never be longer than MAXPATHLEN,
but extra check does not hurt */ but extra check does not hurt */
if (safe_wcscpy(calculate->argv0_path, tmpbuffer, argv0_path_len) < 0) { if (safe_wcscpy(argv0_path, tmpbuffer, argv0_path_len) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
} }
else { else {
/* Interpret relative to program_full_path */ /* Interpret relative to program_full_path */
PyStatus status; PyStatus status;
reduce(calculate->argv0_path); reduce(argv0_path);
status = joinpath(calculate->argv0_path, tmpbuffer, argv0_path_len); status = joinpath(argv0_path, tmpbuffer, argv0_path_len);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
} }
linklen = _Py_wreadlink(calculate->argv0_path, tmpbuffer, buflen); linklen = _Py_wreadlink(argv0_path, tmpbuffer, buflen);
} }
#endif /* HAVE_READLINK */ #endif /* HAVE_READLINK */
reduce(calculate->argv0_path); reduce(argv0_path);
/* At this point, argv0_path is guaranteed to be less than /* At this point, argv0_path is guaranteed to be less than
MAXPATHLEN bytes long. */ MAXPATHLEN bytes long. */
return _PyStatus_OK(); return _PyStatus_OK();
@ -947,7 +958,8 @@ calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_pat
If found, open it for use when searching for prefixes. If found, open it for use when searching for prefixes.
*/ */
static PyStatus static PyStatus
calculate_read_pyenv(PyCalculatePath *calculate) calculate_read_pyenv(PyCalculatePath *calculate,
wchar_t *argv0_path, size_t argv0_path_len)
{ {
PyStatus status; PyStatus status;
wchar_t tmpbuffer[MAXPATHLEN+1]; wchar_t tmpbuffer[MAXPATHLEN+1];
@ -955,7 +967,7 @@ calculate_read_pyenv(PyCalculatePath *calculate)
wchar_t *env_cfg = L"pyvenv.cfg"; wchar_t *env_cfg = L"pyvenv.cfg";
FILE *env_file; FILE *env_file;
if (safe_wcscpy(tmpbuffer, calculate->argv0_path, buflen) < 0) { if (safe_wcscpy(tmpbuffer, argv0_path, buflen) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
@ -986,8 +998,7 @@ calculate_read_pyenv(PyCalculatePath *calculate)
/* Look for a 'home' variable and set argv0_path to it, if found */ /* Look for a 'home' variable and set argv0_path to it, if found */
if (_Py_FindEnvConfigValue(env_file, L"home", tmpbuffer, buflen)) { if (_Py_FindEnvConfigValue(env_file, L"home", tmpbuffer, buflen)) {
if (safe_wcscpy(calculate->argv0_path, tmpbuffer, if (safe_wcscpy(argv0_path, tmpbuffer, argv0_path_len) < 0) {
Py_ARRAY_LENGTH(calculate->argv0_path)) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
} }
@ -997,33 +1008,33 @@ calculate_read_pyenv(PyCalculatePath *calculate)
static PyStatus static PyStatus
calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix) calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix,
wchar_t *zip_path, size_t zip_path_len)
{ {
PyStatus status; PyStatus status;
const size_t zip_path_len = Py_ARRAY_LENGTH(calculate->zip_path); if (safe_wcscpy(zip_path, prefix, zip_path_len) < 0) {
if (safe_wcscpy(calculate->zip_path, prefix, zip_path_len) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
if (calculate->prefix_found > 0) { if (calculate->prefix_found > 0) {
/* Use the reduced prefix returned by Py_GetPrefix() */ /* Use the reduced prefix returned by Py_GetPrefix() */
reduce(calculate->zip_path); reduce(zip_path);
reduce(calculate->zip_path); reduce(zip_path);
} }
else { else {
if (safe_wcscpy(calculate->zip_path, calculate->prefix, zip_path_len) < 0) { if (safe_wcscpy(zip_path, calculate->prefix, zip_path_len) < 0) {
return PATHLEN_ERR(); return PATHLEN_ERR();
} }
} }
status = joinpath(calculate->zip_path, L"lib/python00.zip", zip_path_len); status = joinpath(zip_path, L"lib/python00.zip", zip_path_len);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
/* Replace "00" with version */ /* Replace "00" with version */
size_t bufsz = wcslen(calculate->zip_path); size_t bufsz = wcslen(zip_path);
calculate->zip_path[bufsz - 6] = VERSION[0]; zip_path[bufsz - 6] = VERSION[0];
calculate->zip_path[bufsz - 5] = VERSION[2]; zip_path[bufsz - 5] = VERSION[2];
return _PyStatus_OK(); return _PyStatus_OK();
} }
@ -1031,7 +1042,9 @@ calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix)
static PyStatus static PyStatus
calculate_module_search_path(PyCalculatePath *calculate, calculate_module_search_path(PyCalculatePath *calculate,
_PyPathConfig *pathconfig, _PyPathConfig *pathconfig,
const wchar_t *prefix, const wchar_t *exec_prefix) const wchar_t *prefix,
const wchar_t *exec_prefix,
const wchar_t *zip_path)
{ {
/* Calculate size of return buffer */ /* Calculate size of return buffer */
size_t bufsz = 0; size_t bufsz = 0;
@ -1059,7 +1072,7 @@ calculate_module_search_path(PyCalculatePath *calculate,
defpath = delim + 1; defpath = delim + 1;
} }
bufsz += wcslen(calculate->zip_path) + 1; bufsz += wcslen(zip_path) + 1;
bufsz += wcslen(exec_prefix) + 1; bufsz += wcslen(exec_prefix) + 1;
/* Allocate the buffer */ /* Allocate the buffer */
@ -1076,7 +1089,7 @@ calculate_module_search_path(PyCalculatePath *calculate,
} }
/* Next is the default zip path */ /* Next is the default zip path */
wcscat(buf, calculate->zip_path); wcscat(buf, zip_path);
wcscat(buf, delimiter); wcscat(buf, delimiter);
/* Next goes merge of compile-time $PYTHONPATH with /* Next goes merge of compile-time $PYTHONPATH with
@ -1119,8 +1132,7 @@ calculate_module_search_path(PyCalculatePath *calculate,
static PyStatus static PyStatus
calculate_init(PyCalculatePath *calculate, calculate_init(PyCalculatePath *calculate, const PyConfig *config)
const PyConfig *config)
{ {
size_t len; size_t len;
const char *path = getenv("PATH"); const char *path = getenv("PATH");
@ -1135,6 +1147,7 @@ calculate_init(PyCalculatePath *calculate,
if (!calculate->pythonpath) { if (!calculate->pythonpath) {
return DECODE_LOCALE_ERR("PYTHONPATH define", len); return DECODE_LOCALE_ERR("PYTHONPATH define", len);
} }
calculate->prefix = Py_DecodeLocale(PREFIX, &len); calculate->prefix = Py_DecodeLocale(PREFIX, &len);
if (!calculate->prefix) { if (!calculate->prefix) {
return DECODE_LOCALE_ERR("PREFIX define", len); return DECODE_LOCALE_ERR("PREFIX define", len);
@ -1178,12 +1191,17 @@ calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
} }
} }
status = calculate_argv0_path(calculate, pathconfig->program_full_path); wchar_t argv0_path[MAXPATHLEN+1];
memset(argv0_path, 0, sizeof(argv0_path));
status = calculate_argv0_path(calculate, pathconfig->program_full_path,
argv0_path, Py_ARRAY_LENGTH(argv0_path));
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
status = calculate_read_pyenv(calculate); status = calculate_read_pyenv(calculate,
argv0_path, Py_ARRAY_LENGTH(argv0_path));
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
@ -1191,19 +1209,24 @@ calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
wchar_t prefix[MAXPATHLEN+1]; wchar_t prefix[MAXPATHLEN+1];
memset(prefix, 0, sizeof(prefix)); memset(prefix, 0, sizeof(prefix));
status = calculate_prefix(calculate, pathconfig, status = calculate_prefix(calculate, pathconfig,
argv0_path,
prefix, Py_ARRAY_LENGTH(prefix)); prefix, Py_ARRAY_LENGTH(prefix));
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
status = calculate_zip_path(calculate, prefix); wchar_t zip_path[MAXPATHLEN+1]; /* ".../lib/pythonXY.zip" */
memset(zip_path, 0, sizeof(zip_path));
status = calculate_zip_path(calculate, prefix,
zip_path, Py_ARRAY_LENGTH(zip_path));
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
wchar_t exec_prefix[MAXPATHLEN+1]; wchar_t exec_prefix[MAXPATHLEN+1];
memset(exec_prefix, 0, sizeof(exec_prefix)); memset(exec_prefix, 0, sizeof(exec_prefix));
status = calculate_exec_prefix(calculate, pathconfig, status = calculate_exec_prefix(calculate, pathconfig, argv0_path,
exec_prefix, Py_ARRAY_LENGTH(exec_prefix)); exec_prefix, Py_ARRAY_LENGTH(exec_prefix));
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
@ -1218,50 +1241,60 @@ calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
if (pathconfig->module_search_path == NULL) { if (pathconfig->module_search_path == NULL) {
status = calculate_module_search_path(calculate, pathconfig, status = calculate_module_search_path(calculate, pathconfig,
prefix, exec_prefix); prefix, exec_prefix, zip_path);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
} }
if (pathconfig->prefix == NULL) { if (pathconfig->prefix == NULL) {
status = calculate_reduce_prefix(calculate, prefix, Py_ARRAY_LENGTH(prefix)); status = calculate_set_prefix(calculate, pathconfig, prefix);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
pathconfig->prefix = _PyMem_RawWcsdup(prefix);
if (pathconfig->prefix == NULL) {
return _PyStatus_NO_MEMORY();
}
} }
if (pathconfig->exec_prefix == NULL) { if (pathconfig->exec_prefix == NULL) {
status = calculate_reduce_exec_prefix(calculate, status = calculate_set_exec_prefix(calculate, pathconfig, exec_prefix);
exec_prefix,
Py_ARRAY_LENGTH(exec_prefix));
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
pathconfig->exec_prefix = _PyMem_RawWcsdup(exec_prefix);
if (pathconfig->exec_prefix == NULL) {
return _PyStatus_NO_MEMORY();
}
} }
return _PyStatus_OK(); return _PyStatus_OK();
} }
/* Calculate 'pathconfig' attributes: /* Calculate the Python path configuration.
Inputs:
- PATH environment variable
- Macros: PYTHONPATH, PREFIX, EXEC_PREFIX, VERSION (ex: "3.9").
PREFIX and EXEC_PREFIX are generated by the configure script.
PYTHONPATH macro is the default search path.
- pybuilddir.txt file
- pyvenv.cfg configuration file
- PyConfig fields ('config' function argument):
- pathconfig_warnings
- pythonpath_env (PYTHONPATH environment variable)
- _PyPathConfig fields ('pathconfig' function argument):
- program_name: see config_init_program_name()
- home: Py_SetPythonHome() or PYTHONHOME environment variable
- current working directory: see copy_absolute()
Outputs, 'pathconfig' fields:
- program_full_path - program_full_path
- module_search_path - module_search_path
- prefix - prefix
- exec_prefix - exec_prefix
If an attribute is already set (non NULL), it is left unchanged. */ If a field is already set (non NULL), it is left unchanged. */
PyStatus PyStatus
_PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config) _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
{ {

View file

@ -115,20 +115,21 @@
*/ */
#ifndef LANDMARK #ifndef LANDMARK
#define LANDMARK L"lib\\os.py" # define LANDMARK L"lib\\os.py"
#endif #endif
#define INIT_ERR_BUFFER_OVERFLOW() _PyStatus_ERR("buffer overflow")
typedef struct { typedef struct {
const wchar_t *path_env; /* PATH environment variable */ const wchar_t *path_env; /* PATH environment variable */
const wchar_t *home; /* PYTHONHOME environment variable */ const wchar_t *home; /* PYTHONHOME environment variable */
/* Registry key "Software\Python\PythonCore\PythonPath" */ /* Registry key "Software\Python\PythonCore\X.Y\PythonPath"
where X.Y is the Python version (major.minor) */
wchar_t *machine_path; /* from HKEY_LOCAL_MACHINE */ wchar_t *machine_path; /* from HKEY_LOCAL_MACHINE */
wchar_t *user_path; /* from HKEY_CURRENT_USER */ wchar_t *user_path; /* from HKEY_CURRENT_USER */
wchar_t argv0_path[MAXPATHLEN+1];
wchar_t zip_path[MAXPATHLEN+1];
wchar_t *dll_path; wchar_t *dll_path;
const wchar_t *pythonpath_env; const wchar_t *pythonpath_env;
@ -276,7 +277,10 @@ typedef HRESULT(__stdcall *PPathCchCanonicalizeEx) (PWSTR pszPathOut, size_t cch
PCWSTR pszPathIn, unsigned long dwFlags); PCWSTR pszPathIn, unsigned long dwFlags);
static PPathCchCanonicalizeEx _PathCchCanonicalizeEx; static PPathCchCanonicalizeEx _PathCchCanonicalizeEx;
static PyStatus canonicalize(wchar_t *buffer, const wchar_t *path) /* Call PathCchCanonicalizeEx(path): remove navigation elements such as "."
and ".." to produce a direct, well-formed path. */
static PyStatus
canonicalize(wchar_t *buffer, const wchar_t *path)
{ {
if (buffer == NULL) { if (buffer == NULL) {
return _PyStatus_NO_MEMORY(); return _PyStatus_NO_MEMORY();
@ -295,12 +299,12 @@ static PyStatus canonicalize(wchar_t *buffer, const wchar_t *path)
if (_PathCchCanonicalizeEx) { if (_PathCchCanonicalizeEx) {
if (FAILED(_PathCchCanonicalizeEx(buffer, MAXPATHLEN + 1, path, 0))) { if (FAILED(_PathCchCanonicalizeEx(buffer, MAXPATHLEN + 1, path, 0))) {
return _PyStatus_ERR("buffer overflow in getpathp.c's canonicalize()"); return INIT_ERR_BUFFER_OVERFLOW();
} }
} }
else { else {
if (!PathCanonicalizeW(buffer, path)) { if (!PathCanonicalizeW(buffer, path)) {
return _PyStatus_ERR("buffer overflow in getpathp.c's canonicalize()"); return INIT_ERR_BUFFER_OVERFLOW();
} }
} }
return _PyStatus_OK(); return _PyStatus_OK();
@ -588,12 +592,18 @@ get_program_full_path(_PyPathConfig *pathconfig)
} }
static int static PyStatus
read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path) read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path,
int *found)
{ {
FILE *sp_file = _Py_wfopen(path, L"r"); PyStatus status;
wchar_t *buf = NULL;
wchar_t *wline = NULL;
FILE *sp_file;
sp_file = _Py_wfopen(path, L"r");
if (sp_file == NULL) { if (sp_file == NULL) {
return 0; return _PyStatus_OK();
} }
wcscpy_s(prefix, MAXPATHLEN+1, path); wcscpy_s(prefix, MAXPATHLEN+1, path);
@ -604,15 +614,16 @@ read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path)
size_t bufsiz = MAXPATHLEN; size_t bufsiz = MAXPATHLEN;
size_t prefixlen = wcslen(prefix); size_t prefixlen = wcslen(prefix);
wchar_t *buf = (wchar_t*)PyMem_RawMalloc(bufsiz * sizeof(wchar_t)); buf = (wchar_t*)PyMem_RawMalloc(bufsiz * sizeof(wchar_t));
if (buf == NULL) { if (buf == NULL) {
goto error; status = _PyStatus_NO_MEMORY();
goto done;
} }
buf[0] = '\0'; buf[0] = '\0';
while (!feof(sp_file)) { while (!feof(sp_file)) {
char line[MAXPATHLEN + 1]; char line[MAXPATHLEN + 1];
char *p = fgets(line, MAXPATHLEN + 1, sp_file); char *p = fgets(line, Py_ARRAY_LENGTH(line), sp_file);
if (!p) { if (!p) {
break; break;
} }
@ -631,13 +642,16 @@ read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path)
continue; continue;
} }
else if (strncmp(line, "import ", 7) == 0) { else if (strncmp(line, "import ", 7) == 0) {
Py_FatalError("only 'import site' is supported in ._pth file"); status = _PyStatus_ERR("only 'import site' is supported "
"in ._pth file");
goto done;
} }
DWORD wn = MultiByteToWideChar(CP_UTF8, 0, line, -1, NULL, 0); DWORD wn = MultiByteToWideChar(CP_UTF8, 0, line, -1, NULL, 0);
wchar_t *wline = (wchar_t*)PyMem_RawMalloc((wn + 1) * sizeof(wchar_t)); wchar_t *wline = (wchar_t*)PyMem_RawMalloc((wn + 1) * sizeof(wchar_t));
if (wline == NULL) { if (wline == NULL) {
goto error; status = _PyStatus_NO_MEMORY();
goto done;
} }
wn = MultiByteToWideChar(CP_UTF8, 0, line, -1, wline, wn + 1); wn = MultiByteToWideChar(CP_UTF8, 0, line, -1, wline, wn + 1);
wline[wn] = '\0'; wline[wn] = '\0';
@ -648,8 +662,8 @@ read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path)
wchar_t *tmp = (wchar_t*)PyMem_RawRealloc(buf, (bufsiz + 1) * wchar_t *tmp = (wchar_t*)PyMem_RawRealloc(buf, (bufsiz + 1) *
sizeof(wchar_t)); sizeof(wchar_t));
if (tmp == NULL) { if (tmp == NULL) {
PyMem_RawFree(wline); status = _PyStatus_NO_MEMORY();
goto error; goto done;
} }
buf = tmp; buf = tmp;
} }
@ -663,48 +677,39 @@ read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path)
_Py_BEGIN_SUPPRESS_IPH _Py_BEGIN_SUPPRESS_IPH
result = wcscat_s(buf, bufsiz, prefix); result = wcscat_s(buf, bufsiz, prefix);
_Py_END_SUPPRESS_IPH _Py_END_SUPPRESS_IPH
if (result == EINVAL) { if (result == EINVAL) {
Py_FatalError("invalid argument during ._pth processing"); status = _PyStatus_ERR("invalid argument during ._pth processing");
goto done;
} else if (result == ERANGE) { } else if (result == ERANGE) {
Py_FatalError("buffer overflow during ._pth processing"); status = _PyStatus_ERR("buffer overflow during ._pth processing");
goto done;
} }
wchar_t *b = &buf[usedsiz]; wchar_t *b = &buf[usedsiz];
join(b, wline); join(b, wline);
PyMem_RawFree(wline); PyMem_RawFree(wline);
wline = NULL;
} }
fclose(sp_file);
if (pathconfig->module_search_path == NULL) { if (pathconfig->module_search_path == NULL) {
pathconfig->module_search_path = _PyMem_RawWcsdup(buf); pathconfig->module_search_path = _PyMem_RawWcsdup(buf);
if (pathconfig->module_search_path == NULL) { if (pathconfig->module_search_path == NULL) {
Py_FatalError("out of memory"); status = _PyStatus_NO_MEMORY();
goto done;
} }
} }
PyMem_RawFree(buf);
return 1;
error: *found = 1;
status = _PyStatus_OK();
goto done;
done:
PyMem_RawFree(buf); PyMem_RawFree(buf);
PyMem_RawFree(wline);
fclose(sp_file); fclose(sp_file);
return 0; return status;
}
static PyStatus
calculate_init(PyCalculatePath *calculate, const PyConfig *config)
{
calculate->home = config->home;
calculate->path_env = _wgetenv(L"PATH");
calculate->dll_path = _Py_GetDLLPath();
if (calculate->dll_path == NULL) {
return _PyStatus_NO_MEMORY();
}
calculate->pythonpath_env = config->pythonpath_env;
return _PyStatus_OK();
} }
@ -730,17 +735,17 @@ get_pth_filename(PyCalculatePath *calculate, wchar_t *filename,
} }
static int static PyStatus
calculate_pth_file(PyCalculatePath *calculate, _PyPathConfig *pathconfig, calculate_pth_file(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
wchar_t *prefix) wchar_t *prefix, int *found)
{ {
wchar_t filename[MAXPATHLEN+1]; wchar_t filename[MAXPATHLEN+1];
if (!get_pth_filename(calculate, filename, pathconfig)) { if (!get_pth_filename(calculate, filename, pathconfig)) {
return 0; return _PyStatus_OK();
} }
return read_pth_file(pathconfig, prefix, filename); return read_pth_file(pathconfig, prefix, filename, found);
} }
@ -749,12 +754,13 @@ calculate_pth_file(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
If found, open it for use when searching for prefixes. If found, open it for use when searching for prefixes.
*/ */
static void static void
calculate_pyvenv_file(PyCalculatePath *calculate) calculate_pyvenv_file(PyCalculatePath *calculate,
wchar_t *argv0_path, size_t argv0_path_len)
{ {
wchar_t envbuffer[MAXPATHLEN+1]; wchar_t envbuffer[MAXPATHLEN+1];
const wchar_t *env_cfg = L"pyvenv.cfg"; const wchar_t *env_cfg = L"pyvenv.cfg";
wcscpy_s(envbuffer, MAXPATHLEN+1, calculate->argv0_path); wcscpy_s(envbuffer, MAXPATHLEN+1, argv0_path);
join(envbuffer, env_cfg); join(envbuffer, env_cfg);
FILE *env_file = _Py_wfopen(envbuffer, L"r"); FILE *env_file = _Py_wfopen(envbuffer, L"r");
@ -778,25 +784,25 @@ calculate_pyvenv_file(PyCalculatePath *calculate)
/* Look for a 'home' variable and set argv0_path to it, if found */ /* Look for a 'home' variable and set argv0_path to it, if found */
wchar_t tmpbuffer[MAXPATHLEN+1]; wchar_t tmpbuffer[MAXPATHLEN+1];
if (_Py_FindEnvConfigValue(env_file, L"home", tmpbuffer, MAXPATHLEN)) { if (_Py_FindEnvConfigValue(env_file, L"home", tmpbuffer, MAXPATHLEN)) {
wcscpy_s(calculate->argv0_path, MAXPATHLEN+1, tmpbuffer); wcscpy_s(argv0_path, argv0_path_len, tmpbuffer);
} }
fclose(env_file); fclose(env_file);
} }
#define INIT_ERR_BUFFER_OVERFLOW() _PyStatus_ERR("buffer overflow")
static void static void
calculate_home_prefix(PyCalculatePath *calculate, wchar_t *prefix) calculate_home_prefix(PyCalculatePath *calculate,
const wchar_t *argv0_path,
const wchar_t *zip_path,
wchar_t *prefix)
{ {
if (calculate->home == NULL || *calculate->home == '\0') { if (calculate->home == NULL || *calculate->home == '\0') {
if (calculate->zip_path[0] && exists(calculate->zip_path)) { if (zip_path[0] && exists(zip_path)) {
wcscpy_s(prefix, MAXPATHLEN+1, calculate->zip_path); wcscpy_s(prefix, MAXPATHLEN+1, zip_path);
reduce(prefix); reduce(prefix);
calculate->home = prefix; calculate->home = prefix;
} }
else if (search_for_prefix(prefix, calculate->argv0_path, LANDMARK)) { else if (search_for_prefix(prefix, argv0_path, LANDMARK)) {
calculate->home = prefix; calculate->home = prefix;
} }
else { else {
@ -812,7 +818,9 @@ calculate_home_prefix(PyCalculatePath *calculate, wchar_t *prefix)
static PyStatus static PyStatus
calculate_module_search_path(PyCalculatePath *calculate, calculate_module_search_path(PyCalculatePath *calculate,
_PyPathConfig *pathconfig, _PyPathConfig *pathconfig,
wchar_t *prefix) const wchar_t *argv0_path,
wchar_t *prefix,
const wchar_t *zip_path)
{ {
int skiphome = calculate->home==NULL ? 0 : 1; int skiphome = calculate->home==NULL ? 0 : 1;
#ifdef Py_ENABLE_SHARED #ifdef Py_ENABLE_SHARED
@ -852,14 +860,14 @@ calculate_module_search_path(PyCalculatePath *calculate,
bufsz *= wcslen(calculate->home); bufsz *= wcslen(calculate->home);
} }
bufsz += wcslen(PYTHONPATH) + 1; bufsz += wcslen(PYTHONPATH) + 1;
bufsz += wcslen(calculate->argv0_path) + 1; bufsz += wcslen(argv0_path) + 1;
if (calculate->user_path) { if (calculate->user_path) {
bufsz += wcslen(calculate->user_path) + 1; bufsz += wcslen(calculate->user_path) + 1;
} }
if (calculate->machine_path) { if (calculate->machine_path) {
bufsz += wcslen(calculate->machine_path) + 1; bufsz += wcslen(calculate->machine_path) + 1;
} }
bufsz += wcslen(calculate->zip_path) + 1; bufsz += wcslen(zip_path) + 1;
if (calculate->pythonpath_env != NULL) { if (calculate->pythonpath_env != NULL) {
bufsz += wcslen(calculate->pythonpath_env) + 1; bufsz += wcslen(calculate->pythonpath_env) + 1;
} }
@ -867,7 +875,7 @@ calculate_module_search_path(PyCalculatePath *calculate,
wchar_t *buf, *start_buf; wchar_t *buf, *start_buf;
buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t)); buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t));
if (buf == NULL) { if (buf == NULL) {
Py_FatalError("Can't malloc dynamic PYTHONPATH"); return _PyStatus_NO_MEMORY();
} }
start_buf = buf; start_buf = buf;
@ -879,8 +887,8 @@ calculate_module_search_path(PyCalculatePath *calculate,
buf = wcschr(buf, L'\0'); buf = wcschr(buf, L'\0');
*buf++ = DELIM; *buf++ = DELIM;
} }
if (calculate->zip_path[0]) { if (zip_path[0]) {
if (wcscpy_s(buf, bufsz - (buf - start_buf), calculate->zip_path)) { if (wcscpy_s(buf, bufsz - (buf - start_buf), zip_path)) {
return INIT_ERR_BUFFER_OVERFLOW(); return INIT_ERR_BUFFER_OVERFLOW();
} }
buf = wcschr(buf, L'\0'); buf = wcschr(buf, L'\0');
@ -937,8 +945,8 @@ calculate_module_search_path(PyCalculatePath *calculate,
p = q+1; p = q+1;
} }
} }
if (calculate->argv0_path) { if (argv0_path) {
wcscpy(buf, calculate->argv0_path); wcscpy(buf, argv0_path);
buf = wcschr(buf, L'\0'); buf = wcschr(buf, L'\0');
*buf++ = DELIM; *buf++ = DELIM;
} }
@ -996,28 +1004,40 @@ calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
} }
/* program_full_path guaranteed \0 terminated in MAXPATH+1 bytes. */ /* program_full_path guaranteed \0 terminated in MAXPATH+1 bytes. */
wcscpy_s(calculate->argv0_path, MAXPATHLEN+1, pathconfig->program_full_path); wchar_t argv0_path[MAXPATHLEN+1];
reduce(calculate->argv0_path); memset(argv0_path, 0, sizeof(argv0_path));
wcscpy_s(argv0_path, MAXPATHLEN+1, pathconfig->program_full_path);
reduce(argv0_path);
wchar_t prefix[MAXPATHLEN+1]; wchar_t prefix[MAXPATHLEN+1];
memset(prefix, 0, sizeof(prefix)); memset(prefix, 0, sizeof(prefix));
/* Search for a sys.path file */ /* Search for a sys.path file */
if (calculate_pth_file(calculate, pathconfig, prefix)) { int pth_found = 0;
status = calculate_pth_file(calculate, pathconfig, prefix, &pth_found);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
if (pth_found) {
goto done; goto done;
} }
calculate_pyvenv_file(calculate); calculate_pyvenv_file(calculate, argv0_path, Py_ARRAY_LENGTH(argv0_path));
/* Calculate zip archive path from DLL or exe path */ /* Calculate zip archive path from DLL or exe path */
change_ext(calculate->zip_path, wchar_t zip_path[MAXPATHLEN+1];
memset(zip_path, 0, sizeof(zip_path));
change_ext(zip_path,
calculate->dll_path[0] ? calculate->dll_path : pathconfig->program_full_path, calculate->dll_path[0] ? calculate->dll_path : pathconfig->program_full_path,
L".zip"); L".zip");
calculate_home_prefix(calculate, prefix); calculate_home_prefix(calculate, argv0_path, zip_path, prefix);
if (pathconfig->module_search_path == NULL) { if (pathconfig->module_search_path == NULL) {
status = calculate_module_search_path(calculate, pathconfig, prefix); status = calculate_module_search_path(calculate, pathconfig,
argv0_path, prefix, zip_path);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
@ -1041,6 +1061,24 @@ done:
} }
static PyStatus
calculate_init(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
const PyConfig *config)
{
calculate->home = pathconfig->home;
calculate->path_env = _wgetenv(L"PATH");
calculate->dll_path = _Py_GetDLLPath();
if (calculate->dll_path == NULL) {
return _PyStatus_NO_MEMORY();
}
calculate->pythonpath_env = config->pythonpath_env;
return _PyStatus_OK();
}
static void static void
calculate_free(PyCalculatePath *calculate) calculate_free(PyCalculatePath *calculate)
{ {
@ -1050,7 +1088,24 @@ calculate_free(PyCalculatePath *calculate)
} }
/* Calculate 'pathconfig' attributes: /* Calculate the Python path configuration.
Inputs:
- PyConfig.pythonpath_env: PYTHONPATH environment variable
- _PyPathConfig.home: Py_SetPythonHome() or PYTHONHOME environment variable
- DLL path: _Py_GetDLLPath()
- PATH environment variable
- __PYVENV_LAUNCHER__ environment variable
- GetModuleFileNameW(NULL): fully qualified path of the executable file of
the current process
- .pth configuration file
- pyvenv.cfg configuration file
- Registry key "Software\Python\PythonCore\X.Y\PythonPath"
of HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER where X.Y is the Python
version (major.minor).
Outputs, 'pathconfig' fields:
- base_executable - base_executable
- program_full_path - program_full_path
@ -1060,7 +1115,7 @@ calculate_free(PyCalculatePath *calculate)
- isolated - isolated
- site_import - site_import
If an attribute is already set (non NULL), it is left unchanged. */ If a field is already set (non NULL), it is left unchanged. */
PyStatus PyStatus
_PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config) _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
{ {
@ -1068,7 +1123,7 @@ _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
PyCalculatePath calculate; PyCalculatePath calculate;
memset(&calculate, 0, sizeof(calculate)); memset(&calculate, 0, sizeof(calculate));
status = calculate_init(&calculate, config); status = calculate_init(&calculate, pathconfig, config);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
goto done; goto done;
} }