bpo-45582: Port getpath[p].c to Python (GH-29041)

The getpath.py file is frozen at build time and executed as code over a namespace. It is never imported, nor is it meant to be importable or reusable. However, it should be easier to read, modify, and patch than the previous code.

This commit attempts to preserve every previously tested quirk, but these may be changed in the future to better align platforms.
This commit is contained in:
Steve Dower 2021-12-03 00:08:42 +00:00 committed by GitHub
parent 9f2f7e4226
commit 99fcf15052
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 3686 additions and 3838 deletions

View file

@ -2,6 +2,9 @@
/* Support for dynamic loading of extension modules */
#include "Python.h"
#include "pycore_fileutils.h" // _Py_add_relfile()
#include "pycore_pathconfig.h" // _PyPathConfig_ComputeSysPath0()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#ifdef HAVE_DIRECT_H
#include <direct.h>
@ -160,6 +163,60 @@ static char *GetPythonImport (HINSTANCE hModule)
return NULL;
}
/* Load python3.dll before loading any extension module that might refer
to it. That way, we can be sure that always the python3.dll corresponding
to this python DLL is loaded, not a python3.dll that might be on the path
by chance.
Return whether the DLL was found.
*/
extern HMODULE PyWin_DLLhModule;
static int
_Py_CheckPython3(void)
{
static int python3_checked = 0;
static HANDLE hPython3;
#define MAXPATHLEN 512
wchar_t py3path[MAXPATHLEN+1];
if (python3_checked) {
return hPython3 != NULL;
}
python3_checked = 1;
/* If there is a python3.dll next to the python3y.dll,
use that DLL */
if (PyWin_DLLhModule && GetModuleFileNameW(PyWin_DLLhModule, py3path, MAXPATHLEN)) {
wchar_t *p = wcsrchr(py3path, L'\\');
if (p) {
wcscpy(p + 1, PY3_DLLNAME);
hPython3 = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
if (hPython3 != NULL) {
return 1;
}
}
}
/* If we can locate python3.dll in our application dir,
use that DLL */
hPython3 = LoadLibraryExW(PY3_DLLNAME, NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
if (hPython3 != NULL) {
return 1;
}
/* For back-compat, also search {sys.prefix}\DLLs, though
that has not been a normal install layout for a while */
PyInterpreterState *interp = _PyInterpreterState_GET();
PyConfig *config = (PyConfig*)_PyInterpreterState_GetConfig(interp);
assert(config->prefix);
if (config->prefix) {
wcscpy_s(py3path, MAXPATHLEN, config->prefix);
if (py3path[0] && _Py_add_relfile(py3path, L"DLLs\\" PY3_DLLNAME, MAXPATHLEN) >= 0) {
hPython3 = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
}
}
return hPython3 != NULL;
#undef MAXPATHLEN
}
dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
const char *shortname,
PyObject *pathname, FILE *fp)

View file

@ -2000,13 +2000,28 @@ _Py_wrealpath(const wchar_t *path,
#endif
#ifndef MS_WINDOWS
int
_Py_isabs(const wchar_t *path)
{
#ifdef MS_WINDOWS
const wchar_t *tail;
HRESULT hr = PathCchSkipRoot(path, &tail);
if (FAILED(hr) || path == tail) {
return 0;
}
if (tail == &path[1] && (path[0] == SEP || path[0] == ALTSEP)) {
// Exclude paths with leading SEP
return 0;
}
if (tail == &path[2] && path[1] == L':') {
// Exclude drive-relative paths (e.g. C:filename.ext)
return 0;
}
return 1;
#else
return (path[0] == SEP);
}
#endif
}
/* Get an absolute path.
@ -2017,6 +2032,22 @@ _Py_isabs(const wchar_t *path)
int
_Py_abspath(const wchar_t *path, wchar_t **abspath_p)
{
if (path[0] == '\0' || !wcscmp(path, L".")) {
wchar_t cwd[MAXPATHLEN + 1];
cwd[Py_ARRAY_LENGTH(cwd) - 1] = 0;
if (!_Py_wgetcwd(cwd, Py_ARRAY_LENGTH(cwd) - 1)) {
/* unable to get the current directory */
return -1;
}
*abspath_p = _PyMem_RawWcsdup(cwd);
return 0;
}
if (_Py_isabs(path)) {
*abspath_p = _PyMem_RawWcsdup(path);
return 0;
}
#ifdef MS_WINDOWS
wchar_t woutbuf[MAX_PATH], *woutbufp = woutbuf;
DWORD result;
@ -2028,7 +2059,7 @@ _Py_abspath(const wchar_t *path, wchar_t **abspath_p)
return -1;
}
if (result > Py_ARRAY_LENGTH(woutbuf)) {
if (result >= Py_ARRAY_LENGTH(woutbuf)) {
if ((size_t)result <= (size_t)PY_SSIZE_T_MAX / sizeof(wchar_t)) {
woutbufp = PyMem_RawMalloc((size_t)result * sizeof(wchar_t));
}
@ -2055,11 +2086,6 @@ _Py_abspath(const wchar_t *path, wchar_t **abspath_p)
*abspath_p = _PyMem_RawWcsdup(woutbufp);
return 0;
#else
if (_Py_isabs(path)) {
*abspath_p = _PyMem_RawWcsdup(path);
return 0;
}
wchar_t cwd[MAXPATHLEN + 1];
cwd[Py_ARRAY_LENGTH(cwd) - 1] = 0;
if (!_Py_wgetcwd(cwd, Py_ARRAY_LENGTH(cwd) - 1)) {
@ -2102,7 +2128,8 @@ join_relfile(wchar_t *buffer, size_t bufsize,
const wchar_t *dirname, const wchar_t *relfile)
{
#ifdef MS_WINDOWS
if (FAILED(PathCchCombineEx(buffer, bufsize, dirname, relfile, 0))) {
if (FAILED(PathCchCombineEx(buffer, bufsize, dirname, relfile,
PATHCCH_ALLOW_LONG_PATHS | PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS))) {
return -1;
}
#else
@ -2180,99 +2207,125 @@ _Py_find_basename(const wchar_t *filename)
return 0;
}
/* Remove navigation elements such as "." and "..".
This is mostly a C implementation of posixpath.normpath().
Return 0 on success. Return -1 if "orig" is too big for the buffer. */
int
_Py_normalize_path(const wchar_t *path, wchar_t *buf, const size_t buf_len)
/* In-place path normalisation. Returns the start of the normalized
path, which will be within the original buffer. Guaranteed to not
make the path longer, and will not fail. 'size' is the length of
the path, if known. If -1, the first null character will be assumed
to be the end of the path. */
wchar_t *
_Py_normpath(wchar_t *path, Py_ssize_t size)
{
assert(path && *path != L'\0');
assert(*path == SEP); // an absolute path
if (wcslen(path) + 1 >= buf_len) {
return -1;
if (!path[0] || size == 0) {
return path;
}
wchar_t lastC = L'\0';
wchar_t *p1 = path;
wchar_t *pEnd = size >= 0 ? &path[size] : NULL;
wchar_t *p2 = path;
wchar_t *minP2 = path;
int dots = -1;
int check_leading = 1;
const wchar_t *buf_start = buf;
wchar_t *buf_next = buf;
// The resulting filename will never be longer than path.
for (const wchar_t *remainder = path; *remainder != L'\0'; remainder++) {
wchar_t c = *remainder;
buf_next[0] = c;
buf_next++;
if (c == SEP) {
assert(dots <= 2);
if (dots == 2) {
// Turn "/x/y/../z" into "/x/z".
buf_next -= 4; // "/../"
assert(*buf_next == SEP);
// We cap it off at the root, so "/../spam" becomes "/spam".
if (buf_next == buf_start) {
buf_next++;
}
else {
// Move to the previous SEP in the buffer.
while (*(buf_next - 1) != SEP) {
assert(buf_next != buf_start);
buf_next--;
}
}
}
else if (dots == 1) {
// Turn "/./" into "/".
buf_next -= 2; // "./"
assert(*(buf_next - 1) == SEP);
}
else if (dots == 0) {
// Turn "//" into "/".
buf_next--;
assert(*(buf_next - 1) == SEP);
if (check_leading) {
if (buf_next - 1 == buf && *(remainder + 1) != SEP) {
// Leave a leading "//" alone, unless "///...".
buf_next++;
buf_start++;
}
check_leading = 0;
}
}
dots = 0;
#define IS_END(x) (pEnd ? (x) == pEnd : !*(x))
#ifdef ALTSEP
#define IS_SEP(x) (*(x) == SEP || *(x) == ALTSEP)
#else
#define IS_SEP(x) (*(x) == SEP)
#endif
#define SEP_OR_END(x) (IS_SEP(x) || IS_END(x))
// Skip leading '.\'
if (p1[0] == L'.' && IS_SEP(&p1[1])) {
path = &path[2];
while (IS_SEP(path) && !IS_END(path)) {
path++;
}
else {
check_leading = 0;
if (dots >= 0) {
if (c == L'.' && dots < 2) {
dots++;
}
else {
dots = -1;
}
p1 = p2 = minP2 = path;
lastC = SEP;
}
#ifdef MS_WINDOWS
// Skip past drive segment and update minP2
else if (p1[0] && p1[1] == L':') {
*p2++ = *p1++;
*p2++ = *p1++;
minP2 = p2;
lastC = L':';
}
// Skip past all \\-prefixed paths, including \\?\, \\.\,
// and network paths, including the first segment.
else if (IS_SEP(&p1[0]) && IS_SEP(&p1[1])) {
int sepCount = 2;
*p2++ = SEP;
*p2++ = SEP;
p1 += 2;
for (; !IS_END(p1) && sepCount; ++p1) {
if (IS_SEP(p1)) {
--sepCount;
*p2++ = lastC = SEP;
} else {
*p2++ = lastC = *p1;
}
}
minP2 = p2;
}
#else
// Skip past two leading SEPs
else if (IS_SEP(&p1[0]) && IS_SEP(&p1[1]) && !IS_SEP(&p1[2])) {
*p2++ = *p1++;
*p2++ = *p1++;
minP2 = p2;
lastC = SEP;
}
#endif /* MS_WINDOWS */
/* if pEnd is specified, check that. Else, check for null terminator */
for (; !IS_END(p1); ++p1) {
wchar_t c = *p1;
#ifdef ALTSEP
if (c == ALTSEP) {
c = SEP;
}
#endif
if (lastC == SEP) {
if (c == L'.') {
int sep_at_1 = SEP_OR_END(&p1[1]);
int sep_at_2 = !sep_at_1 && SEP_OR_END(&p1[2]);
if (sep_at_2 && p1[1] == L'.') {
wchar_t *p3 = p2;
while (p3 != minP2 && *--p3 == SEP) { }
while (p3 != minP2 && *(p3 - 1) != SEP) { --p3; }
if (p3[0] == L'.' && p3[1] == L'.' && IS_SEP(&p3[2])) {
// Previous segment is also ../, so append instead
*p2++ = L'.';
*p2++ = L'.';
lastC = L'.';
} else if (p3[0] == SEP) {
// Absolute path, so absorb segment
p2 = p3 + 1;
} else {
p2 = p3;
}
p1 += 1;
} else if (sep_at_1) {
} else {
*p2++ = lastC = c;
}
} else if (c == SEP) {
} else {
*p2++ = lastC = c;
}
} else {
*p2++ = lastC = c;
}
}
*p2 = L'\0';
if (p2 != minP2) {
while (--p2 != minP2 && *p2 == SEP) {
*p2 = L'\0';
}
}
if (dots >= 0) {
// Strip any trailing dots and trailing slash.
buf_next -= dots + 1; // "/" or "/." or "/.."
assert(*buf_next == SEP);
if (buf_next == buf_start) {
// Leave the leading slash for root.
buf_next++;
}
else {
if (dots == 2) {
// Move to the previous SEP in the buffer.
do {
assert(buf_next != buf_start);
buf_next--;
} while (*(buf_next) != SEP);
}
}
}
*buf_next = L'\0';
return 0;
#undef SEP_OR_END
#undef IS_SEP
#undef IS_END
return path;
}

View file

@ -22,11 +22,6 @@
# endif
#endif
#ifndef PLATLIBDIR
# error "PLATLIBDIR macro must be defined"
#endif
/* --- Command line options --------------------------------------- */
/* Short usage message (with %s for argv0) */
@ -632,7 +627,6 @@ config_check_consistency(const PyConfig *config)
assert(config->parse_argv >= 0);
assert(config->configure_c_stdio >= 0);
assert(config->buffered_stdio >= 0);
assert(config->program_name != NULL);
assert(_PyWideStringList_CheckConsistency(&config->orig_argv));
assert(_PyWideStringList_CheckConsistency(&config->argv));
/* sys.argv must be non-empty: empty argv is replaced with [''] */
@ -641,7 +635,6 @@ config_check_consistency(const PyConfig *config)
assert(_PyWideStringList_CheckConsistency(&config->warnoptions));
assert(_PyWideStringList_CheckConsistency(&config->module_search_paths));
assert(config->module_search_paths_set >= 0);
assert(config->platlibdir != NULL);
assert(config->filesystem_encoding != NULL);
assert(config->filesystem_errors != NULL);
assert(config->stdio_encoding != NULL);
@ -740,6 +733,7 @@ _PyConfig_InitCompatConfig(PyConfig *config)
config->legacy_windows_stdio = -1;
#endif
config->use_frozen_modules = -1;
config->_is_python_build = 0;
config->code_debug_ranges = 1;
}
@ -962,6 +956,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
COPY_ATTR(_isolated_interpreter);
COPY_ATTR(use_frozen_modules);
COPY_WSTRLIST(orig_argv);
COPY_ATTR(_is_python_build);
#undef COPY_ATTR
#undef COPY_WSTR_ATTR
@ -1066,6 +1061,7 @@ _PyConfig_AsDict(const PyConfig *config)
SET_ITEM_INT(_isolated_interpreter);
SET_ITEM_WSTRLIST(orig_argv);
SET_ITEM_INT(use_frozen_modules);
SET_ITEM_INT(_is_python_build);
return dict;
@ -1350,6 +1346,7 @@ _PyConfig_FromDict(PyConfig *config, PyObject *dict)
GET_UINT(_init_main);
GET_UINT(_isolated_interpreter);
GET_UINT(use_frozen_modules);
GET_UINT(_is_python_build);
#undef CHECK_VALUE
#undef GET_UINT
@ -1489,117 +1486,6 @@ config_set_global_vars(const PyConfig *config)
}
/* Get the program name: use PYTHONEXECUTABLE and __PYVENV_LAUNCHER__
environment variables on macOS if available. */
static PyStatus
config_init_program_name(PyConfig *config)
{
PyStatus status;
/* If Py_SetProgramName() was called, use its value */
const 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 _PyStatus_NO_MEMORY();
}
return _PyStatus_OK();
}
#ifdef __APPLE__
/* On MacOS X, when the Python interpreter is embedded in an
application bundle, it gets executed by a bootstrapping script
that does os.execve() with an argv[0] that's different from the
actual Python executable. This is needed to keep the Finder happy,
or rather, to work around Apple's overly strict requirements of
the process name. However, we still need a usable sys.executable,
so the actual executable path is passed in an environment variable.
See Lib/plat-mac/bundlebuilder.py for details about the bootstrap
script. */
const char *p = config_get_env(config, "PYTHONEXECUTABLE");
if (p != NULL) {
status = CONFIG_SET_BYTES_STR(config, &config->program_name, p,
"PYTHONEXECUTABLE environment variable");
if (_PyStatus_EXCEPTION(status)) {
return status;
}
return _PyStatus_OK();
}
#ifdef WITH_NEXT_FRAMEWORK
else {
const char* pyvenv_launcher = getenv("__PYVENV_LAUNCHER__");
if (pyvenv_launcher && *pyvenv_launcher) {
/* Used by Mac/Tools/pythonw.c to forward
* the argv0 of the stub executable
*/
status = CONFIG_SET_BYTES_STR(config,
&config->program_name,
pyvenv_launcher,
"__PYVENV_LAUNCHER__ environment variable");
if (_PyStatus_EXCEPTION(status)) {
return status;
}
/*
* This environment variable is used to communicate between
* the stub launcher and the real interpreter and isn't needed
* beyond this point.
*
* Clean up to avoid problems when launching other programs
* later on.
*/
(void)unsetenv("__PYVENV_LAUNCHER__");
return _PyStatus_OK();
}
}
#endif /* WITH_NEXT_FRAMEWORK */
#endif /* __APPLE__ */
/* Use argv[0] if available and non-empty */
const PyWideStringList *argv = &config->argv;
if (argv->length >= 1 && argv->items[0][0] != L'\0') {
config->program_name = _PyMem_RawWcsdup(argv->items[0]);
if (config->program_name == NULL) {
return _PyStatus_NO_MEMORY();
}
return _PyStatus_OK();
}
/* Last fall back: hardcoded name */
#ifdef MS_WINDOWS
const wchar_t *default_program_name = L"python";
#else
const wchar_t *default_program_name = L"python3";
#endif
status = PyConfig_SetString(config, &config->program_name,
default_program_name);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
return _PyStatus_OK();
}
static PyStatus
config_init_executable(PyConfig *config)
{
assert(config->executable == NULL);
/* If Py_SetProgramFullPath() was called, use its value */
const wchar_t *program_full_path = _Py_path_config.program_full_path;
if (program_full_path != NULL) {
PyStatus status = PyConfig_SetString(config,
&config->executable,
program_full_path);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
return _PyStatus_OK();
}
return _PyStatus_OK();
}
static const wchar_t*
config_get_xoption(const PyConfig *config, wchar_t *name)
{
@ -1618,25 +1504,6 @@ config_get_xoption_value(const PyConfig *config, wchar_t *name)
}
static PyStatus
config_init_home(PyConfig *config)
{
assert(config->home == NULL);
/* If Py_SetPythonHome() was called, use its value */
wchar_t *home = _Py_path_config.home;
if (home) {
PyStatus status = PyConfig_SetString(config, &config->home, home);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
return _PyStatus_OK();
}
return CONFIG_GET_ENV_DUP(config, &config->home,
L"PYTHONHOME", "PYTHONHOME");
}
static PyStatus
config_init_hash_seed(PyConfig *config)
{
@ -2092,44 +1959,6 @@ config_init_fs_encoding(PyConfig *config, const PyPreConfig *preconfig)
}
/* Determine if the current build is a "development" build (e.g. running
out of the source tree) or not.
A return value of -1 indicates that we do not know.
*/
static int
is_dev_env(PyConfig *config)
{
// This should only ever get called early in runtime initialization,
// before the global path config is written. Otherwise we would
// use Py_GetProgramFullPath() and _Py_GetStdlibDir().
assert(config != NULL);
const wchar_t *executable = config->executable;
const wchar_t *stdlib = config->stdlib_dir;
if (executable == NULL || *executable == L'\0' ||
stdlib == NULL || *stdlib == L'\0') {
// _PyPathConfig_Calculate() hasn't run yet.
return -1;
}
size_t len = _Py_find_basename(executable);
if (wcscmp(executable + len, L"python") != 0 &&
wcscmp(executable + len, L"python.exe") != 0) {
return 0;
}
/* If dirname() is the same for both then it is a dev build. */
if (len != _Py_find_basename(stdlib)) {
return 0;
}
// We do not bother normalizing the two filenames first since
// for config_init_import() is does the right thing as-is.
if (wcsncmp(stdlib, executable, len) != 0) {
return 0;
}
return 1;
}
static PyStatus
config_init_import(PyConfig *config, int compute_path_config)
{
@ -2144,10 +1973,7 @@ config_init_import(PyConfig *config, int compute_path_config)
if (config->use_frozen_modules < 0) {
const wchar_t *value = config_get_xoption_value(config, L"frozen_modules");
if (value == NULL) {
int isdev = is_dev_env(config);
if (isdev >= 0) {
config->use_frozen_modules = !isdev;
}
config->use_frozen_modules = !config->_is_python_build;
}
else if (wcscmp(value, L"on") == 0) {
config->use_frozen_modules = 1;
@ -2246,28 +2072,6 @@ config_read(PyConfig *config, int compute_path_config)
return status;
}
if (config->home == NULL) {
status = config_init_home(config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}
if (config->executable == NULL) {
status = config_init_executable(config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}
if(config->platlibdir == NULL) {
status = CONFIG_SET_BYTES_STR(config, &config->platlibdir, PLATLIBDIR,
"PLATLIBDIR macro");
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}
if (config->_install_importlib) {
status = config_init_import(config, compute_path_config);
if (_PyStatus_EXCEPTION(status)) {
@ -2890,13 +2694,6 @@ config_read_cmdline(PyConfig *config)
config->parse_argv = 1;
}
if (config->program_name == NULL) {
status = config_init_program_name(config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}
if (config->parse_argv == 1) {
Py_ssize_t opt_index;
status = config_parse_cmdline(config, &cmdline_warnoptions, &opt_index);
@ -3076,7 +2873,7 @@ done:
PyStatus
PyConfig_Read(PyConfig *config)
{
return _PyConfig_Read(config, 1);
return _PyConfig_Read(config, 0);
}
@ -3124,16 +2921,6 @@ _Py_GetConfigsAsDict(void)
}
Py_CLEAR(dict);
/* path config */
dict = _PyPathConfig_AsDict();
if (dict == NULL) {
goto error;
}
if (PyDict_SetItemString(result, "path_config", dict) < 0) {
goto error;
}
Py_CLEAR(dict);
return result;
error:
@ -3199,6 +2986,7 @@ _Py_DumpPathConfig(PyThreadState *tstate)
PySys_WriteStderr(" environment = %i\n", config->use_environment);
PySys_WriteStderr(" user site = %i\n", config->user_site_directory);
PySys_WriteStderr(" import site = %i\n", config->site_import);
PySys_WriteStderr(" is in build tree = %i\n", config->_is_python_build);
DUMP_CONFIG("stdlib dir", stdlib_dir);
#undef DUMP_CONFIG

View file

@ -1,6 +1,7 @@
/* Path configuration like module_search_path (sys.path) */
#include "Python.h"
#include "marshal.h" // PyMarshal_ReadObjectFromString
#include "osdefs.h" // DELIM
#include "pycore_initconfig.h"
#include "pycore_fileutils.h"
@ -9,6 +10,8 @@
#include <wchar.h>
#ifdef MS_WINDOWS
# include <windows.h> // GetFullPathNameW(), MAX_PATH
# include <pathcch.h>
# include <shlwapi.h>
#endif
#ifdef __cplusplus
@ -16,86 +19,36 @@ extern "C" {
#endif
/* External interface */
/* Stored values set by C API functions */
typedef struct _PyPathConfig {
/* Full path to the Python program */
wchar_t *program_full_path;
wchar_t *prefix;
wchar_t *exec_prefix;
wchar_t *stdlib_dir;
/* Set by Py_SetPath */
wchar_t *module_search_path;
/* Set by _PyPathConfig_UpdateGlobal */
wchar_t *calculated_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}
_PyPathConfig _Py_path_config = _PyPathConfig_INIT;
static int
copy_wstr(wchar_t **dst, const wchar_t *src)
const wchar_t *
_PyPathConfig_GetGlobalModuleSearchPath(void)
{
assert(*dst == NULL);
if (src != NULL) {
*dst = _PyMem_RawWcsdup(src);
if (*dst == NULL) {
return -1;
}
}
else {
*dst = NULL;
}
return 0;
}
static void
pathconfig_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); \
ATTR = NULL; \
} while (0)
CLEAR(config->program_full_path);
CLEAR(config->prefix);
CLEAR(config->exec_prefix);
CLEAR(config->stdlib_dir);
CLEAR(config->module_search_path);
CLEAR(config->program_name);
CLEAR(config->home);
#ifdef MS_WINDOWS
CLEAR(config->base_executable);
#endif
#undef CLEAR
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
}
static PyStatus
pathconfig_copy(_PyPathConfig *config, const _PyPathConfig *config2)
{
pathconfig_clear(config);
#define COPY_ATTR(ATTR) \
do { \
if (copy_wstr(&config->ATTR, config2->ATTR) < 0) { \
return _PyStatus_NO_MEMORY(); \
} \
} while (0)
COPY_ATTR(program_full_path);
COPY_ATTR(prefix);
COPY_ATTR(exec_prefix);
COPY_ATTR(module_search_path);
COPY_ATTR(stdlib_dir);
COPY_ATTR(program_name);
COPY_ATTR(home);
#ifdef MS_WINDOWS
config->isolated = config2->isolated;
config->site_import = config2->site_import;
COPY_ATTR(base_executable);
#endif
#undef COPY_ATTR
return _PyStatus_OK();
return _Py_path_config.module_search_path;
}
@ -105,374 +58,132 @@ _PyPathConfig_ClearGlobal(void)
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
pathconfig_clear(&_Py_path_config);
#define CLEAR(ATTR) \
do { \
PyMem_RawFree(_Py_path_config.ATTR); \
_Py_path_config.ATTR = NULL; \
} while (0)
CLEAR(program_full_path);
CLEAR(prefix);
CLEAR(exec_prefix);
CLEAR(stdlib_dir);
CLEAR(module_search_path);
CLEAR(calculated_module_search_path);
CLEAR(program_name);
CLEAR(home);
#undef CLEAR
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
}
static wchar_t*
_PyWideStringList_Join(const PyWideStringList *list, wchar_t sep)
PyStatus
_PyPathConfig_ReadGlobal(PyConfig *config)
{
size_t len = 1; /* NUL terminator */
for (Py_ssize_t i=0; i < list->length; i++) {
if (i != 0) {
len++;
}
len += wcslen(list->items[i]);
}
PyStatus status = _PyStatus_OK();
wchar_t *text = PyMem_RawMalloc(len * sizeof(wchar_t));
if (text == NULL) {
return NULL;
}
wchar_t *str = text;
for (Py_ssize_t i=0; i < list->length; i++) {
wchar_t *path = list->items[i];
if (i != 0) {
*str++ = sep;
}
len = wcslen(path);
memcpy(str, path, len * sizeof(wchar_t));
str += len;
}
*str = L'\0';
#define COPY(ATTR) \
do { \
if (_Py_path_config.ATTR && !config->ATTR) { \
status = PyConfig_SetString(config, &config->ATTR, _Py_path_config.ATTR); \
if (_PyStatus_EXCEPTION(status)) goto done; \
} \
} while (0)
return text;
#define COPY2(ATTR, SRCATTR) \
do { \
if (_Py_path_config.SRCATTR && !config->ATTR) { \
status = PyConfig_SetString(config, &config->ATTR, _Py_path_config.SRCATTR); \
if (_PyStatus_EXCEPTION(status)) goto done; \
} \
} while (0)
COPY(prefix);
COPY(exec_prefix);
COPY(stdlib_dir);
COPY(program_name);
COPY(home);
COPY2(executable, program_full_path);
// module_search_path must be initialised - not read
#undef COPY
#undef COPY2
done:
return status;
}
static PyStatus
pathconfig_set_from_config(_PyPathConfig *pathconfig, const PyConfig *config)
PyStatus
_PyPathConfig_UpdateGlobal(const PyConfig *config)
{
PyStatus status;
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
if (config->module_search_paths_set) {
PyMem_RawFree(pathconfig->module_search_path);
pathconfig->module_search_path = _PyWideStringList_Join(&config->module_search_paths, DELIM);
if (pathconfig->module_search_path == NULL) {
goto no_memory;
}
}
#define COPY(ATTR) \
do { \
if (config->ATTR) { \
PyMem_RawFree(_Py_path_config.ATTR); \
_Py_path_config.ATTR = _PyMem_RawWcsdup(config->ATTR); \
if (!_Py_path_config.ATTR) goto error; \
} \
} while (0)
#define COPY_CONFIG(PATH_ATTR, CONFIG_ATTR) \
if (config->CONFIG_ATTR) { \
PyMem_RawFree(pathconfig->PATH_ATTR); \
pathconfig->PATH_ATTR = NULL; \
if (copy_wstr(&pathconfig->PATH_ATTR, config->CONFIG_ATTR) < 0) { \
goto no_memory; \
} \
#define COPY2(ATTR, SRCATTR) \
do { \
if (config->SRCATTR) { \
PyMem_RawFree(_Py_path_config.ATTR); \
_Py_path_config.ATTR = _PyMem_RawWcsdup(config->SRCATTR); \
if (!_Py_path_config.ATTR) goto error; \
} \
} while (0)
COPY(prefix);
COPY(exec_prefix);
COPY(stdlib_dir);
COPY(program_name);
COPY(home);
COPY2(program_full_path, executable);
#undef COPY
#undef COPY2
PyMem_RawFree(_Py_path_config.module_search_path);
_Py_path_config.module_search_path = NULL;
PyMem_RawFree(_Py_path_config.calculated_module_search_path);
_Py_path_config.calculated_module_search_path = NULL;
do {
size_t cch = 1;
for (Py_ssize_t i = 0; i < config->module_search_paths.length; ++i) {
cch += 1 + wcslen(config->module_search_paths.items[i]);
}
COPY_CONFIG(program_full_path, executable);
COPY_CONFIG(prefix, prefix);
COPY_CONFIG(exec_prefix, exec_prefix);
COPY_CONFIG(stdlib_dir, stdlib_dir);
COPY_CONFIG(program_name, program_name);
COPY_CONFIG(home, home);
#ifdef MS_WINDOWS
COPY_CONFIG(base_executable, base_executable);
#endif
wchar_t *path = (wchar_t*)PyMem_RawMalloc(sizeof(wchar_t) * cch);
if (!path) {
goto error;
}
wchar_t *p = path;
for (Py_ssize_t i = 0; i < config->module_search_paths.length; ++i) {
wcscpy(p, config->module_search_paths.items[i]);
p = wcschr(p, L'\0');
*p++ = DELIM;
*p = L'\0';
}
#undef COPY_CONFIG
do {
*p = L'\0';
} while (p != path && *--p == DELIM);
_Py_path_config.calculated_module_search_path = path;
} while (0);
status = _PyStatus_OK();
goto done;
no_memory:
status = _PyStatus_NO_MEMORY();
done:
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
return status;
}
PyObject *
_PyPathConfig_AsDict(void)
{
PyObject *dict = PyDict_New();
if (dict == NULL) {
return NULL;
}
#define SET_ITEM(KEY, EXPR) \
do { \
PyObject *obj = (EXPR); \
if (obj == NULL) { \
goto fail; \
} \
int res = PyDict_SetItemString(dict, KEY, obj); \
Py_DECREF(obj); \
if (res < 0) { \
goto fail; \
} \
} while (0)
#define SET_ITEM_STR(KEY) \
SET_ITEM(#KEY, \
(_Py_path_config.KEY \
? PyUnicode_FromWideChar(_Py_path_config.KEY, -1) \
: (Py_INCREF(Py_None), Py_None)))
#define SET_ITEM_INT(KEY) \
SET_ITEM(#KEY, PyLong_FromLong(_Py_path_config.KEY))
SET_ITEM_STR(program_full_path);
SET_ITEM_STR(prefix);
SET_ITEM_STR(exec_prefix);
SET_ITEM_STR(module_search_path);
SET_ITEM_STR(stdlib_dir);
SET_ITEM_STR(program_name);
SET_ITEM_STR(home);
#ifdef MS_WINDOWS
SET_ITEM_INT(isolated);
SET_ITEM_INT(site_import);
SET_ITEM_STR(base_executable);
{
wchar_t py3path[MAX_PATH];
HMODULE hPython3 = GetModuleHandleW(PY3_DLLNAME);
PyObject *obj;
if (hPython3
&& GetModuleFileNameW(hPython3, py3path, Py_ARRAY_LENGTH(py3path)))
{
obj = PyUnicode_FromWideChar(py3path, -1);
if (obj == NULL) {
goto fail;
}
}
else {
obj = Py_None;
Py_INCREF(obj);
}
if (PyDict_SetItemString(dict, "python3_dll", obj) < 0) {
Py_DECREF(obj);
goto fail;
}
Py_DECREF(obj);
}
#endif
#undef SET_ITEM
#undef SET_ITEM_STR
#undef SET_ITEM_INT
return dict;
fail:
Py_DECREF(dict);
return NULL;
}
PyStatus
_PyConfig_WritePathConfig(const PyConfig *config)
{
return pathconfig_set_from_config(&_Py_path_config, config);
}
static PyStatus
config_init_module_search_paths(PyConfig *config, _PyPathConfig *pathconfig)
{
assert(!config->module_search_paths_set);
_PyWideStringList_Clear(&config->module_search_paths);
const wchar_t *sys_path = pathconfig->module_search_path;
const wchar_t delim = DELIM;
while (1) {
const wchar_t *p = wcschr(sys_path, delim);
if (p == NULL) {
p = sys_path + wcslen(sys_path); /* End of string */
}
size_t path_len = (p - sys_path);
wchar_t *path = PyMem_RawMalloc((path_len + 1) * sizeof(wchar_t));
if (path == NULL) {
return _PyStatus_NO_MEMORY();
}
memcpy(path, sys_path, path_len * sizeof(wchar_t));
path[path_len] = L'\0';
PyStatus status = PyWideStringList_Append(&config->module_search_paths, path);
PyMem_RawFree(path);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
if (*p == '\0') {
break;
}
sys_path = p + 1;
}
config->module_search_paths_set = 1;
return _PyStatus_OK();
}
/* Calculate the path configuration:
- exec_prefix
- module_search_path
- stdlib_dir
- 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_init(_PyPathConfig *pathconfig, const PyConfig *config,
int compute_path_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 (compute_path_config) {
status = _PyPathConfig_Calculate(pathconfig, config);
}
done:
error:
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
return status;
return _PyStatus_NO_MEMORY();
}
static PyStatus
config_init_pathconfig(PyConfig *config, int compute_path_config)
{
_PyPathConfig pathconfig = _PyPathConfig_INIT;
PyStatus status;
status = pathconfig_init(&pathconfig, config, compute_path_config);
if (_PyStatus_EXCEPTION(status)) {
goto done;
}
if (!config->module_search_paths_set
&& pathconfig.module_search_path != NULL)
{
status = config_init_module_search_paths(config, &pathconfig);
if (_PyStatus_EXCEPTION(status)) {
goto done;
}
}
#define COPY_ATTR(PATH_ATTR, CONFIG_ATTR) \
if (config->CONFIG_ATTR == NULL && pathconfig.PATH_ATTR != NULL) { \
if (copy_wstr(&config->CONFIG_ATTR, pathconfig.PATH_ATTR) < 0) { \
goto no_memory; \
} \
}
#ifdef MS_WINDOWS
if (config->executable != NULL && config->base_executable == NULL) {
/* If executable is set explicitly in the configuration,
ignore calculated base_executable: _PyConfig_InitPathConfig()
will copy executable to base_executable */
}
else {
COPY_ATTR(base_executable, base_executable);
}
#endif
COPY_ATTR(program_full_path, executable);
COPY_ATTR(prefix, prefix);
COPY_ATTR(exec_prefix, exec_prefix);
COPY_ATTR(stdlib_dir, stdlib_dir);
#undef COPY_ATTR
#ifdef MS_WINDOWS
/* If a ._pth file is found: isolated and site_import are overridden */
if (pathconfig.isolated != -1) {
config->isolated = pathconfig.isolated;
}
if (pathconfig.site_import != -1) {
config->site_import = pathconfig.site_import;
}
#endif
status = _PyStatus_OK();
goto done;
no_memory:
status = _PyStatus_NO_MEMORY();
done:
pathconfig_clear(&pathconfig);
return status;
}
PyStatus
_PyConfig_InitPathConfig(PyConfig *config, int compute_path_config)
{
/* Do we need to calculate the path? */
if (!config->module_search_paths_set
|| config->executable == NULL
|| config->prefix == NULL
|| config->exec_prefix == NULL)
{
PyStatus status = config_init_pathconfig(config, compute_path_config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}
if (config->base_prefix == NULL && config->prefix != NULL) {
if (copy_wstr(&config->base_prefix, config->prefix) < 0) {
return _PyStatus_NO_MEMORY();
}
}
if (config->base_exec_prefix == NULL && config->exec_prefix != NULL) {
if (copy_wstr(&config->base_exec_prefix,
config->exec_prefix) < 0) {
return _PyStatus_NO_MEMORY();
}
}
if (config->base_executable == NULL && config->executable != NULL) {
if (copy_wstr(&config->base_executable,
config->executable) < 0) {
return _PyStatus_NO_MEMORY();
}
}
return _PyStatus_OK();
}
/* External interface */
static void _Py_NO_RETURN
path_out_of_memory(const char *func)
{
@ -483,7 +194,7 @@ void
Py_SetPath(const wchar_t *path)
{
if (path == NULL) {
pathconfig_clear(&_Py_path_config);
_PyPathConfig_ClearGlobal();
return;
}
@ -494,6 +205,7 @@ Py_SetPath(const wchar_t *path)
PyMem_RawFree(_Py_path_config.exec_prefix);
PyMem_RawFree(_Py_path_config.stdlib_dir);
PyMem_RawFree(_Py_path_config.module_search_path);
PyMem_RawFree(_Py_path_config.calculated_module_search_path);
_Py_path_config.prefix = _PyMem_RawWcsdup(L"");
_Py_path_config.exec_prefix = _PyMem_RawWcsdup(L"");
@ -505,6 +217,7 @@ Py_SetPath(const wchar_t *path)
_Py_path_config.stdlib_dir = _PyMem_RawWcsdup(L"");
}
_Py_path_config.module_search_path = _PyMem_RawWcsdup(path);
_Py_path_config.calculated_module_search_path = NULL;
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
@ -521,19 +234,19 @@ Py_SetPath(const wchar_t *path)
void
Py_SetPythonHome(const wchar_t *home)
{
if (home == NULL) {
return;
}
int has_value = home && home[0];
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
PyMem_RawFree(_Py_path_config.home);
_Py_path_config.home = _PyMem_RawWcsdup(home);
if (has_value) {
_Py_path_config.home = _PyMem_RawWcsdup(home);
}
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
if (_Py_path_config.home == NULL) {
if (has_value && _Py_path_config.home == NULL) {
path_out_of_memory(__func__);
}
}
@ -542,19 +255,19 @@ Py_SetPythonHome(const wchar_t *home)
void
Py_SetProgramName(const wchar_t *program_name)
{
if (program_name == NULL || program_name[0] == L'\0') {
return;
}
int has_value = program_name && program_name[0];
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);
if (has_value) {
_Py_path_config.program_name = _PyMem_RawWcsdup(program_name);
}
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
if (_Py_path_config.program_name == NULL) {
if (has_value && _Py_path_config.program_name == NULL) {
path_out_of_memory(__func__);
}
}
@ -562,19 +275,19 @@ Py_SetProgramName(const wchar_t *program_name)
void
_Py_SetProgramFullPath(const wchar_t *program_full_path)
{
if (program_full_path == NULL || program_full_path[0] == L'\0') {
return;
}
int has_value = program_full_path && program_full_path[0];
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
PyMem_RawFree(_Py_path_config.program_full_path);
_Py_path_config.program_full_path = _PyMem_RawWcsdup(program_full_path);
if (has_value) {
_Py_path_config.program_full_path = _PyMem_RawWcsdup(program_full_path);
}
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
if (_Py_path_config.program_full_path == NULL) {
if (has_value && _Py_path_config.program_full_path == NULL) {
path_out_of_memory(__func__);
}
}
@ -583,7 +296,12 @@ _Py_SetProgramFullPath(const wchar_t *program_full_path)
wchar_t *
Py_GetPath(void)
{
return _Py_path_config.module_search_path;
/* If the user has provided a path, return that */
if (_Py_path_config.module_search_path) {
return _Py_path_config.module_search_path;
}
/* If we have already done calculations, return the calculated path */
return _Py_path_config.calculated_module_search_path;
}
@ -632,6 +350,8 @@ Py_GetProgramName(void)
return _Py_path_config.program_name;
}
/* Compute module search path from argv[0] or the current working
directory ("-m module" case) which will be prepended to sys.argv:
sys.path[0].
@ -772,73 +492,6 @@ _PyPathConfig_ComputeSysPath0(const PyWideStringList *argv, PyObject **path0_p)
}
#ifdef MS_WINDOWS
#define WCSTOK wcstok_s
#else
#define WCSTOK wcstok
#endif
/* Search for a prefix value in an environment file (pyvenv.cfg).
- If found, copy it into *value_p: string which must be freed by
PyMem_RawFree().
- If not found, *value_p is set to NULL.
*/
PyStatus
_Py_FindEnvConfigValue(FILE *env_file, const wchar_t *key,
wchar_t **value_p)
{
*value_p = NULL;
char buffer[MAXPATHLEN * 2 + 1]; /* allow extra for key, '=', etc. */
buffer[Py_ARRAY_LENGTH(buffer)-1] = '\0';
while (!feof(env_file)) {
char * p = fgets(buffer, Py_ARRAY_LENGTH(buffer) - 1, env_file);
if (p == NULL) {
break;
}
size_t n = strlen(p);
if (p[n - 1] != '\n') {
/* line has overflowed - bail */
break;
}
if (p[0] == '#') {
/* Comment - skip */
continue;
}
wchar_t *tmpbuffer = _Py_DecodeUTF8_surrogateescape(buffer, n, NULL);
if (tmpbuffer) {
wchar_t * state;
wchar_t * tok = WCSTOK(tmpbuffer, L" \t\r\n", &state);
if ((tok != NULL) && !wcscmp(tok, key)) {
tok = WCSTOK(NULL, L" \t", &state);
if ((tok != NULL) && !wcscmp(tok, L"=")) {
tok = WCSTOK(NULL, L"\r\n", &state);
if (tok != NULL) {
*value_p = _PyMem_RawWcsdup(tok);
PyMem_RawFree(tmpbuffer);
if (*value_p == NULL) {
return _PyStatus_NO_MEMORY();
}
/* found */
return _PyStatus_OK();
}
}
}
PyMem_RawFree(tmpbuffer);
}
}
/* not found */
return _PyStatus_OK();
}
#ifdef __cplusplus
}
#endif

View file

@ -459,7 +459,7 @@ interpreter_update_config(PyThreadState *tstate, int only_update_path_config)
}
if (_Py_IsMainInterpreter(tstate->interp)) {
PyStatus status = _PyConfig_WritePathConfig(config);
PyStatus status = _PyPathConfig_UpdateGlobal(config);
if (_PyStatus_EXCEPTION(status)) {
_PyErr_SetFromPyStatus(status);
return -1;
@ -488,7 +488,7 @@ _PyInterpreterState_SetConfig(const PyConfig *src_config)
goto done;
}
status = PyConfig_Read(&config);
status = _PyConfig_Read(&config, 1);
if (_PyStatus_EXCEPTION(status)) {
_PyErr_SetFromPyStatus(status);
goto done;
@ -549,7 +549,7 @@ pyinit_core_reconfigure(_PyRuntimeState *runtime,
config = _PyInterpreterState_GetConfig(interp);
if (config->_install_importlib) {
status = _PyConfig_WritePathConfig(config);
status = _PyPathConfig_UpdateGlobal(config);
if (_PyStatus_EXCEPTION(status)) {
return status;
}