mirror of
https://github.com/python/cpython
synced 2024-11-02 14:48:31 +00:00
99fcf15052
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.
497 lines
12 KiB
C
497 lines
12 KiB
C
/* 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"
|
|
#include "pycore_pathconfig.h"
|
|
#include "pycore_pymem.h" // _PyMem_SetDefaultAllocator()
|
|
#include <wchar.h>
|
|
#ifdef MS_WINDOWS
|
|
# include <windows.h> // GetFullPathNameW(), MAX_PATH
|
|
# include <pathcch.h>
|
|
# include <shlwapi.h>
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
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;
|
|
|
|
|
|
const wchar_t *
|
|
_PyPathConfig_GetGlobalModuleSearchPath(void)
|
|
{
|
|
return _Py_path_config.module_search_path;
|
|
}
|
|
|
|
|
|
void
|
|
_PyPathConfig_ClearGlobal(void)
|
|
{
|
|
PyMemAllocatorEx old_alloc;
|
|
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
|
|
|
#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);
|
|
}
|
|
|
|
PyStatus
|
|
_PyPathConfig_ReadGlobal(PyConfig *config)
|
|
{
|
|
PyStatus status = _PyStatus_OK();
|
|
|
|
#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)
|
|
|
|
#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;
|
|
}
|
|
|
|
PyStatus
|
|
_PyPathConfig_UpdateGlobal(const PyConfig *config)
|
|
{
|
|
PyMemAllocatorEx old_alloc;
|
|
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
|
|
|
#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 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]);
|
|
}
|
|
|
|
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';
|
|
}
|
|
|
|
do {
|
|
*p = L'\0';
|
|
} while (p != path && *--p == DELIM);
|
|
_Py_path_config.calculated_module_search_path = path;
|
|
} while (0);
|
|
|
|
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
|
return _PyStatus_OK();
|
|
|
|
error:
|
|
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
|
return _PyStatus_NO_MEMORY();
|
|
}
|
|
|
|
|
|
static void _Py_NO_RETURN
|
|
path_out_of_memory(const char *func)
|
|
{
|
|
_Py_FatalErrorFunc(func, "out of memory");
|
|
}
|
|
|
|
void
|
|
Py_SetPath(const wchar_t *path)
|
|
{
|
|
if (path == NULL) {
|
|
_PyPathConfig_ClearGlobal();
|
|
return;
|
|
}
|
|
|
|
PyMemAllocatorEx old_alloc;
|
|
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
|
|
|
PyMem_RawFree(_Py_path_config.prefix);
|
|
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"");
|
|
// XXX Copy this from the new module_search_path?
|
|
if (_Py_path_config.home != NULL) {
|
|
_Py_path_config.stdlib_dir = _PyMem_RawWcsdup(_Py_path_config.home);
|
|
}
|
|
else {
|
|
_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);
|
|
|
|
if (_Py_path_config.prefix == NULL
|
|
|| _Py_path_config.exec_prefix == NULL
|
|
|| _Py_path_config.stdlib_dir == NULL
|
|
|| _Py_path_config.module_search_path == NULL)
|
|
{
|
|
path_out_of_memory(__func__);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
Py_SetPythonHome(const wchar_t *home)
|
|
{
|
|
int has_value = home && home[0];
|
|
|
|
PyMemAllocatorEx old_alloc;
|
|
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
|
|
|
PyMem_RawFree(_Py_path_config.home);
|
|
if (has_value) {
|
|
_Py_path_config.home = _PyMem_RawWcsdup(home);
|
|
}
|
|
|
|
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
|
|
|
if (has_value && _Py_path_config.home == NULL) {
|
|
path_out_of_memory(__func__);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
Py_SetProgramName(const wchar_t *program_name)
|
|
{
|
|
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);
|
|
if (has_value) {
|
|
_Py_path_config.program_name = _PyMem_RawWcsdup(program_name);
|
|
}
|
|
|
|
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
|
|
|
if (has_value && _Py_path_config.program_name == NULL) {
|
|
path_out_of_memory(__func__);
|
|
}
|
|
}
|
|
|
|
void
|
|
_Py_SetProgramFullPath(const wchar_t *program_full_path)
|
|
{
|
|
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);
|
|
if (has_value) {
|
|
_Py_path_config.program_full_path = _PyMem_RawWcsdup(program_full_path);
|
|
}
|
|
|
|
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
|
|
|
if (has_value && _Py_path_config.program_full_path == NULL) {
|
|
path_out_of_memory(__func__);
|
|
}
|
|
}
|
|
|
|
|
|
wchar_t *
|
|
Py_GetPath(void)
|
|
{
|
|
/* 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;
|
|
}
|
|
|
|
|
|
wchar_t *
|
|
_Py_GetStdlibDir(void)
|
|
{
|
|
wchar_t *stdlib_dir = _Py_path_config.stdlib_dir;
|
|
if (stdlib_dir != NULL && stdlib_dir[0] != L'\0') {
|
|
return stdlib_dir;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
wchar_t *
|
|
Py_GetPrefix(void)
|
|
{
|
|
return _Py_path_config.prefix;
|
|
}
|
|
|
|
|
|
wchar_t *
|
|
Py_GetExecPrefix(void)
|
|
{
|
|
return _Py_path_config.exec_prefix;
|
|
}
|
|
|
|
|
|
wchar_t *
|
|
Py_GetProgramFullPath(void)
|
|
{
|
|
return _Py_path_config.program_full_path;
|
|
}
|
|
|
|
|
|
wchar_t*
|
|
Py_GetPythonHome(void)
|
|
{
|
|
return _Py_path_config.home;
|
|
}
|
|
|
|
|
|
wchar_t *
|
|
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].
|
|
|
|
Return 1 if the path is correctly resolved and written into *path0_p.
|
|
|
|
Return 0 if it fails to resolve the full path. For example, return 0 if the
|
|
current working directory has been removed (bpo-36236) or if argv is empty.
|
|
|
|
Raise an exception and return -1 on error.
|
|
*/
|
|
int
|
|
_PyPathConfig_ComputeSysPath0(const PyWideStringList *argv, PyObject **path0_p)
|
|
{
|
|
assert(_PyWideStringList_CheckConsistency(argv));
|
|
|
|
if (argv->length == 0) {
|
|
/* Leave sys.path unchanged if sys.argv is empty */
|
|
return 0;
|
|
}
|
|
|
|
wchar_t *argv0 = argv->items[0];
|
|
int have_module_arg = (wcscmp(argv0, L"-m") == 0);
|
|
int have_script_arg = (!have_module_arg && (wcscmp(argv0, L"-c") != 0));
|
|
|
|
wchar_t *path0 = argv0;
|
|
Py_ssize_t n = 0;
|
|
|
|
#ifdef HAVE_REALPATH
|
|
wchar_t fullpath[MAXPATHLEN];
|
|
#elif defined(MS_WINDOWS)
|
|
wchar_t fullpath[MAX_PATH];
|
|
#endif
|
|
|
|
if (have_module_arg) {
|
|
#if defined(HAVE_REALPATH) || defined(MS_WINDOWS)
|
|
if (!_Py_wgetcwd(fullpath, Py_ARRAY_LENGTH(fullpath))) {
|
|
return 0;
|
|
}
|
|
path0 = fullpath;
|
|
#else
|
|
path0 = L".";
|
|
#endif
|
|
n = wcslen(path0);
|
|
}
|
|
|
|
#ifdef HAVE_READLINK
|
|
wchar_t link[MAXPATHLEN + 1];
|
|
int nr = 0;
|
|
wchar_t path0copy[2 * MAXPATHLEN + 1];
|
|
|
|
if (have_script_arg) {
|
|
nr = _Py_wreadlink(path0, link, Py_ARRAY_LENGTH(link));
|
|
}
|
|
if (nr > 0) {
|
|
/* It's a symlink */
|
|
link[nr] = '\0';
|
|
if (link[0] == SEP) {
|
|
path0 = link; /* Link to absolute path */
|
|
}
|
|
else if (wcschr(link, SEP) == NULL) {
|
|
/* Link without path */
|
|
}
|
|
else {
|
|
/* Must join(dirname(path0), link) */
|
|
wchar_t *q = wcsrchr(path0, SEP);
|
|
if (q == NULL) {
|
|
/* path0 without path */
|
|
path0 = link;
|
|
}
|
|
else {
|
|
/* Must make a copy, path0copy has room for 2 * MAXPATHLEN */
|
|
wcsncpy(path0copy, path0, MAXPATHLEN);
|
|
q = wcsrchr(path0copy, SEP);
|
|
wcsncpy(q+1, link, MAXPATHLEN);
|
|
q[MAXPATHLEN + 1] = L'\0';
|
|
path0 = path0copy;
|
|
}
|
|
}
|
|
}
|
|
#endif /* HAVE_READLINK */
|
|
|
|
wchar_t *p = NULL;
|
|
|
|
#if SEP == '\\'
|
|
/* Special case for Microsoft filename syntax */
|
|
if (have_script_arg) {
|
|
wchar_t *q;
|
|
#if defined(MS_WINDOWS)
|
|
/* Replace the first element in argv with the full path. */
|
|
wchar_t *ptemp;
|
|
if (GetFullPathNameW(path0,
|
|
Py_ARRAY_LENGTH(fullpath),
|
|
fullpath,
|
|
&ptemp)) {
|
|
path0 = fullpath;
|
|
}
|
|
#endif
|
|
p = wcsrchr(path0, SEP);
|
|
/* Test for alternate separator */
|
|
q = wcsrchr(p ? p : path0, '/');
|
|
if (q != NULL)
|
|
p = q;
|
|
if (p != NULL) {
|
|
n = p + 1 - path0;
|
|
if (n > 1 && p[-1] != ':')
|
|
n--; /* Drop trailing separator */
|
|
}
|
|
}
|
|
#else
|
|
/* All other filename syntaxes */
|
|
if (have_script_arg) {
|
|
#if defined(HAVE_REALPATH)
|
|
if (_Py_wrealpath(path0, fullpath, Py_ARRAY_LENGTH(fullpath))) {
|
|
path0 = fullpath;
|
|
}
|
|
#endif
|
|
p = wcsrchr(path0, SEP);
|
|
}
|
|
if (p != NULL) {
|
|
n = p + 1 - path0;
|
|
#if SEP == '/' /* Special case for Unix filename syntax */
|
|
if (n > 1) {
|
|
/* Drop trailing separator */
|
|
n--;
|
|
}
|
|
#endif /* Unix */
|
|
}
|
|
#endif /* All others */
|
|
|
|
PyObject *path0_obj = PyUnicode_FromWideChar(path0, n);
|
|
if (path0_obj == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
*path0_p = path0_obj;
|
|
return 1;
|
|
}
|
|
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|