summaryrefslogtreecommitdiffstats
path: root/Modules/getpath.c
diff options
context:
space:
mode:
authorSteve Dower <steve.dower@python.org>2021-12-03 00:08:42 (GMT)
committerGitHub <noreply@github.com>2021-12-03 00:08:42 (GMT)
commit99fcf1505218464c489d419d4500f126b6d6dc28 (patch)
treea9d607d854e943b3651248eadbe2f31f8c410021 /Modules/getpath.c
parent9f2f7e42269db74a89fc8cd74d82a875787f01d7 (diff)
downloadcpython-99fcf1505218464c489d419d4500f126b6d6dc28.zip
cpython-99fcf1505218464c489d419d4500f126b6d6dc28.tar.gz
cpython-99fcf1505218464c489d419d4500f126b6d6dc28.tar.bz2
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.
Diffstat (limited to 'Modules/getpath.c')
-rw-r--r--Modules/getpath.c2112
1 files changed, 715 insertions, 1397 deletions
diff --git a/Modules/getpath.c b/Modules/getpath.c
index 4dbd502..32d5db9 100644
--- a/Modules/getpath.c
+++ b/Modules/getpath.c
@@ -1,1617 +1,935 @@
/* Return the initial module search path. */
#include "Python.h"
-#include "pycore_fileutils.h"
+#include "marshal.h" // PyMarshal_ReadObjectFromString
+#include "osdefs.h" // DELIM
#include "pycore_initconfig.h"
+#include "pycore_fileutils.h"
#include "pycore_pathconfig.h"
-#include "osdefs.h" // DELIM
+#include "pycore_pymem.h" // _PyMem_SetDefaultAllocator()
+#include <wchar.h>
-#include <stdlib.h> // getenv()
-#include <string.h>
-#include <sys/types.h>
+#ifdef MS_WINDOWS
+# include <windows.h> // GetFullPathNameW(), MAX_PATH
+# include <pathcch.h>
+#endif
#ifdef __APPLE__
# include <mach-o/dyld.h>
#endif
-/* Search in some common locations for the associated Python libraries.
- *
- * Two directories must be found, the platform independent directory
- * (prefix), containing the common .py and .pyc files, and the platform
- * dependent directory (exec_prefix), containing the shared library
- * modules. Note that prefix and exec_prefix can be the same directory,
- * but for some installations, they are different.
- *
- * Py_GetPath() carries out separate searches for prefix and exec_prefix.
- * Each search tries a number of different locations until a ``landmark''
- * file or directory is found. If no prefix or exec_prefix is found, a
- * warning message is issued and the preprocessor defined PREFIX and
- * EXEC_PREFIX are used (even though they will not work); python carries on
- * as best as is possible, but most imports will fail.
- *
- * Before any searches are done, the location of the executable is
- * determined. If argv[0] has one or more slashes in it, it is used
- * unchanged. Otherwise, it must have been invoked from the shell's path,
- * so we search $PATH for the named executable and use that. If the
- * executable was not found on $PATH (or there was no $PATH environment
- * variable), the original argv[0] string is used.
- *
- * Next, the executable location is examined to see if it is a symbolic
- * link. If so, the link is chased (correctly interpreting a relative
- * pathname if one is found) and the directory of the link target is used.
- *
- * Finally, argv0_path is set to the directory containing the executable
- * (i.e. the last component is stripped).
- *
- * With argv0_path in hand, we perform a number of steps. The same steps
- * are performed for prefix and for exec_prefix, but with a different
- * landmark.
- *
- * Step 1. Are we running python out of the build directory? This is
- * checked by looking for a different kind of landmark relative to
- * argv0_path. For prefix, the landmark's path is derived from the VPATH
- * preprocessor variable (taking into account that its value is almost, but
- * not quite, what we need). For exec_prefix, the landmark is
- * pybuilddir.txt. If the landmark is found, we're done.
- *
- * For the remaining steps, the prefix landmark will always be
- * lib/python$VERSION/os.py and the exec_prefix will always be
- * lib/python$VERSION/lib-dynload, where $VERSION is Python's version
- * number as supplied by the Makefile. Note that this means that no more
- * build directory checking is performed; if the first step did not find
- * the landmarks, the assumption is that python is running from an
- * installed setup.
- *
- * Step 2. See if the $PYTHONHOME environment variable points to the
- * installed location of the Python libraries. If $PYTHONHOME is set, then
- * it points to prefix and exec_prefix. $PYTHONHOME can be a single
- * directory, which is used for both, or the prefix and exec_prefix
- * directories separated by a colon.
- *
- * Step 3. Try to find prefix and exec_prefix relative to argv0_path,
- * backtracking up the path until it is exhausted. This is the most common
- * step to succeed. Note that if prefix and exec_prefix are different,
- * exec_prefix is more likely to be found; however if exec_prefix is a
- * subdirectory of prefix, both will be found.
- *
- * Step 4. Search the directories pointed to by the preprocessor variables
- * PREFIX and EXEC_PREFIX. These are supplied by the Makefile but can be
- * passed in as options to the configure script.
- *
- * That's it!
- *
- * Well, almost. Once we have determined prefix and exec_prefix, the
- * preprocessor variable PYTHONPATH is used to construct a path. Each
- * relative path on PYTHONPATH is prefixed with prefix. Then the directory
- * containing the shared library modules is appended. The environment
- * variable $PYTHONPATH is inserted in front of it all. Finally, the
- * prefix and exec_prefix globals are tweaked so they reflect the values
- * expected by other code, by stripping the "lib/python$VERSION/..." stuff
- * off. If either points to the build directory, the globals are reset to
- * the corresponding preprocessor variables (so sys.prefix will reflect the
- * installation location, even though sys.path points into the build
- * directory). This seems to make more sense given that currently the only
- * known use of sys.prefix and sys.exec_prefix is for the ILU installation
- * process to find the installed Python tree.
- *
- * An embedding application can use Py_SetPath() to override all of
- * these automatic path computations.
- *
- * NOTE: Windows MSVC builds use PC/getpathp.c instead!
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
+/* Reference the precompiled getpath.py */
+#include "getpath.h"
#if (!defined(PREFIX) || !defined(EXEC_PREFIX) \
- || !defined(VERSION) || !defined(VPATH))
-#error "PREFIX, EXEC_PREFIX, VERSION and VPATH macros must be defined"
+ || !defined(VERSION) || !defined(VPATH) \
+ || !defined(PLATLIBDIR))
+#error "PREFIX, EXEC_PREFIX, VERSION, VPATH and PLATLIBDIR macros must be defined"
#endif
-#ifndef LANDMARK
-#define LANDMARK L"os.py"
+#if !defined(PYTHONPATH)
+#define PYTHONPATH NULL
#endif
-#define BUILD_LANDMARK L"Modules/Setup.local"
-
-#define PATHLEN_ERR() _PyStatus_ERR("path configuration: path too long")
-
-typedef struct {
- wchar_t *path_env; /* PATH environment variable */
-
- wchar_t *pythonpath_macro; /* PYTHONPATH macro */
- wchar_t *prefix_macro; /* PREFIX macro */
- wchar_t *exec_prefix_macro; /* EXEC_PREFIX macro */
- wchar_t *vpath_macro; /* VPATH macro */
-
- wchar_t *lib_python; /* <platlibdir> / "pythonX.Y" */
-
- int prefix_found; /* found platform independent libraries? */
- int exec_prefix_found; /* found the platform dependent libraries? */
-
- int warnings;
- const wchar_t *pythonpath_env;
- const wchar_t *platlibdir;
-
- wchar_t *argv0_path;
- wchar_t *zip_path;
- wchar_t *prefix;
- wchar_t *exec_prefix;
-} PyCalculatePath;
-
-static const wchar_t delimiter[2] = {DELIM, '\0'};
-static const wchar_t separator[2] = {SEP, '\0'};
-
-
-static void
-reduce(wchar_t *dir)
-{
- size_t i = wcslen(dir);
- while (i > 0 && dir[i] != SEP) {
- --i;
- }
- dir[i] = '\0';
-}
-
+#if !defined(PYDEBUGEXT)
+#define PYDEBUGEXT NULL
+#endif
-/* Is file, not directory */
-static int
-isfile(const wchar_t *filename)
-{
- struct stat buf;
- if (_Py_wstat(filename, &buf) != 0) {
- return 0;
- }
- if (!S_ISREG(buf.st_mode)) {
- return 0;
- }
- return 1;
-}
+#if !defined(PYWINVER)
+#ifdef MS_DLL_ID
+#define PYWINVER MS_DLL_ID
+#else
+#define PYWINVER NULL
+#endif
+#endif
+#if !defined(EXE_SUFFIX)
+#if defined(MS_WINDOWS) || defined(__CYGWIN__) || defined(__MINGW32__)
+#define EXE_SUFFIX L".exe"
+#else
+#define EXE_SUFFIX NULL
+#endif
+#endif
-/* Is executable file */
-static int
-isxfile(const wchar_t *filename)
-{
- struct stat buf;
- if (_Py_wstat(filename, &buf) != 0) {
- return 0;
- }
- if (!S_ISREG(buf.st_mode)) {
- return 0;
- }
- if ((buf.st_mode & 0111) == 0) {
- return 0;
- }
- return 1;
-}
+/* HELPER FUNCTIONS for getpath.py */
-/* Is directory */
-static int
-isdir(const wchar_t *filename)
+static PyObject *
+getpath_abspath(PyObject *Py_UNUSED(self), PyObject *args)
{
- struct stat buf;
- if (_Py_wstat(filename, &buf) != 0) {
- return 0;
+ PyObject *r = NULL;
+ PyObject *pathobj;
+ const wchar_t *path;
+ if (!PyArg_ParseTuple(args, "U", &pathobj)) {
+ return NULL;
}
- if (!S_ISDIR(buf.st_mode)) {
- return 0;
+ Py_ssize_t len;
+ path = PyUnicode_AsWideCharString(pathobj, &len);
+ if (path) {
+ wchar_t *abs;
+ if (_Py_abspath(path, &abs) == 0 && abs) {
+ r = PyUnicode_FromWideChar(_Py_normpath(abs, -1), -1);
+ PyMem_RawFree((void *)abs);
+ } else {
+ PyErr_SetString(PyExc_OSError, "failed to make path absolute");
+ }
+ PyMem_Free((void *)path);
}
- return 1;
+ return r;
}
-/* Add a path component, by appending stuff to buffer.
- buflen: 'buffer' length in characters including trailing NUL.
-
- If path2 is empty:
-
- - if path doesn't end with SEP and is not empty, add SEP to path
- - otherwise, do nothing. */
-static PyStatus
-joinpath(wchar_t *path, const wchar_t *path2, size_t path_len)
+static PyObject *
+getpath_basename(PyObject *Py_UNUSED(self), PyObject *args)
{
- if (_Py_isabs(path2)) {
- if (wcslen(path2) >= path_len) {
- return PATHLEN_ERR();
- }
- wcscpy(path, path2);
- }
- else {
- if (_Py_add_relfile(path, path2, path_len) < 0) {
- return PATHLEN_ERR();
- }
- return _PyStatus_OK();
+ const char *path;
+ if (!PyArg_ParseTuple(args, "s", &path)) {
+ return NULL;
}
- return _PyStatus_OK();
+ const char *name = strrchr(path, SEP);
+ return PyUnicode_FromString(name ? name + 1 : path);
}
-static wchar_t*
-substring(const wchar_t *str, size_t len)
+static PyObject *
+getpath_dirname(PyObject *Py_UNUSED(self), PyObject *args)
{
- wchar_t *substr = PyMem_RawMalloc((len + 1) * sizeof(wchar_t));
- if (substr == NULL) {
+ const char *path;
+ if (!PyArg_ParseTuple(args, "s", &path)) {
return NULL;
}
-
- if (len) {
- memcpy(substr, str, len * sizeof(wchar_t));
+ const char *name = strrchr(path, SEP);
+ if (!name) {
+ return PyUnicode_FromStringAndSize(NULL, 0);
}
- substr[len] = L'\0';
- return substr;
+ return PyUnicode_FromStringAndSize(path, (name - path));
}
-static wchar_t*
-joinpath2(const wchar_t *path, const wchar_t *path2)
+static PyObject *
+getpath_isabs(PyObject *Py_UNUSED(self), PyObject *args)
{
- if (_Py_isabs(path2)) {
- return _PyMem_RawWcsdup(path2);
+ PyObject *r = NULL;
+ PyObject *pathobj;
+ const wchar_t *path;
+ if (!PyArg_ParseTuple(args, "U", &pathobj)) {
+ return NULL;
}
- return _Py_join_relfile(path, path2);
-}
-
-
-static inline int
-safe_wcscpy(wchar_t *dst, const wchar_t *src, size_t n)
-{
- size_t srclen = wcslen(src);
- if (n <= srclen) {
- dst[0] = L'\0';
- return -1;
+ path = PyUnicode_AsWideCharString(pathobj, NULL);
+ if (path) {
+ r = _Py_isabs(path) ? Py_True : Py_False;
+ PyMem_Free((void *)path);
}
- memcpy(dst, src, (srclen + 1) * sizeof(wchar_t));
- return 0;
+ Py_XINCREF(r);
+ return r;
}
-/* copy_absolute requires that path be allocated at least
- 'abs_path_len' characters (including trailing NUL). */
-static PyStatus
-copy_absolute(wchar_t *abs_path, const wchar_t *path, size_t abs_path_len)
+static PyObject *
+getpath_hassuffix(PyObject *Py_UNUSED(self), PyObject *args)
{
- if (_Py_isabs(path)) {
- if (safe_wcscpy(abs_path, path, abs_path_len) < 0) {
- return PATHLEN_ERR();
- }
+ PyObject *r = NULL;
+ PyObject *pathobj;
+ PyObject *suffixobj;
+ const wchar_t *path;
+ const wchar_t *suffix;
+ if (!PyArg_ParseTuple(args, "UU", &pathobj, &suffixobj)) {
+ return NULL;
}
- else {
- if (!_Py_wgetcwd(abs_path, abs_path_len)) {
- /* unable to get the current directory */
- if (safe_wcscpy(abs_path, path, abs_path_len) < 0) {
- return PATHLEN_ERR();
+ Py_ssize_t len, suffixLen;
+ path = PyUnicode_AsWideCharString(pathobj, &len);
+ if (path) {
+ suffix = PyUnicode_AsWideCharString(suffixobj, &suffixLen);
+ if (suffix) {
+ if (suffixLen < len ||
+#ifdef MS_WINDOWS
+ wcsicmp(&path[len - suffixLen], suffix) != 0
+#else
+ wcscmp(&path[len - suffixLen], suffix) != 0
+#endif
+ ) {
+ r = Py_False;
+ } else {
+ r = Py_True;
}
- return _PyStatus_OK();
- }
- if (path[0] == '.' && path[1] == SEP) {
- path += 2;
- }
- PyStatus status = joinpath(abs_path, path, abs_path_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
+ Py_INCREF(r);
+ PyMem_Free((void *)suffix);
}
+ PyMem_Free((void *)path);
}
- return _PyStatus_OK();
-}
-
-
-/* path_len: path length in characters including trailing NUL */
-static PyStatus
-absolutize(wchar_t **path_p)
-{
- assert(!_Py_isabs(*path_p));
-
- wchar_t abs_path[MAXPATHLEN+1];
- wchar_t *path = *path_p;
-
- PyStatus status = copy_absolute(abs_path, path, Py_ARRAY_LENGTH(abs_path));
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
-
- PyMem_RawFree(*path_p);
- *path_p = _PyMem_RawWcsdup(abs_path);
- if (*path_p == NULL) {
- return _PyStatus_NO_MEMORY();
- }
- return _PyStatus_OK();
+ return r;
}
-/* Is module -- check for .pyc too */
-static PyStatus
-ismodule(const wchar_t *path, int *result)
+static PyObject *
+getpath_isdir(PyObject *Py_UNUSED(self), PyObject *args)
{
- wchar_t *filename = joinpath2(path, LANDMARK);
- if (filename == NULL) {
- return _PyStatus_NO_MEMORY();
- }
-
- if (isfile(filename)) {
- PyMem_RawFree(filename);
- *result = 1;
- return _PyStatus_OK();
- }
-
- /* Check for the compiled version of prefix. */
- size_t len = wcslen(filename);
- wchar_t *pyc = PyMem_RawMalloc((len + 2) * sizeof(wchar_t));
- if (pyc == NULL) {
- PyMem_RawFree(filename);
- return _PyStatus_NO_MEMORY();
+ PyObject *r = NULL;
+ PyObject *pathobj;
+ const wchar_t *path;
+ if (!PyArg_ParseTuple(args, "U", &pathobj)) {
+ return NULL;
}
-
- memcpy(pyc, filename, len * sizeof(wchar_t));
- pyc[len] = L'c';
- pyc[len + 1] = L'\0';
- *result = isfile(pyc);
-
- PyMem_RawFree(filename);
- PyMem_RawFree(pyc);
-
- return _PyStatus_OK();
-}
-
-
-#if defined(__CYGWIN__) || defined(__MINGW32__)
-#ifndef EXE_SUFFIX
-#define EXE_SUFFIX L".exe"
+ path = PyUnicode_AsWideCharString(pathobj, NULL);
+ if (path) {
+#ifdef MS_WINDOWS
+ r = (GetFileAttributesW(path) & FILE_ATTRIBUTE_DIRECTORY) ? Py_True : Py_False;
+#else
+ struct stat st;
+ r = (_Py_wstat(path, &st) == 0) && S_ISDIR(st.st_mode) ? Py_True : Py_False;
#endif
-
-/* pathlen: 'path' length in characters including trailing NUL */
-static PyStatus
-add_exe_suffix(wchar_t **progpath_p)
-{
- wchar_t *progpath = *progpath_p;
-
- /* Check for already have an executable suffix */
- size_t n = wcslen(progpath);
- size_t s = wcslen(EXE_SUFFIX);
- if (wcsncasecmp(EXE_SUFFIX, progpath + n - s, s) == 0) {
- return _PyStatus_OK();
- }
-
- wchar_t *progpath2 = PyMem_RawMalloc((n + s + 1) * sizeof(wchar_t));
- if (progpath2 == NULL) {
- return _PyStatus_NO_MEMORY();
- }
-
- memcpy(progpath2, progpath, n * sizeof(wchar_t));
- memcpy(progpath2 + n, EXE_SUFFIX, s * sizeof(wchar_t));
- progpath2[n+s] = L'\0';
-
- if (isxfile(progpath2)) {
- PyMem_RawFree(*progpath_p);
- *progpath_p = progpath2;
- }
- else {
- PyMem_RawFree(progpath2);
+ PyMem_Free((void *)path);
}
- return _PyStatus_OK();
+ Py_XINCREF(r);
+ return r;
}
-#endif
-/* search_for_prefix requires that argv0_path be no more than MAXPATHLEN
- bytes long.
-*/
-static PyStatus
-search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
- wchar_t *prefix, size_t prefix_len, int *found)
+static PyObject *
+getpath_isfile(PyObject *Py_UNUSED(self), PyObject *args)
{
- PyStatus status;
-
- /* If PYTHONHOME is set, we believe it unconditionally */
- if (pathconfig->home) {
- /* Path: <home> / <lib_python> */
- if (safe_wcscpy(prefix, pathconfig->home, prefix_len) < 0) {
- return PATHLEN_ERR();
- }
- wchar_t *delim = wcschr(prefix, DELIM);
- if (delim) {
- *delim = L'\0';
- }
- status = joinpath(prefix, calculate->lib_python, prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- *found = 1;
- return _PyStatus_OK();
- }
-
- /* Check to see if argv0_path is in the build directory
-
- Path: <argv0_path> / <BUILD_LANDMARK define> */
- wchar_t *path = joinpath2(calculate->argv0_path, BUILD_LANDMARK);
- if (path == NULL) {
- return _PyStatus_NO_MEMORY();
- }
-
- int is_build_dir = isfile(path);
- PyMem_RawFree(path);
-
- if (is_build_dir) {
- /* argv0_path is the build directory (BUILD_LANDMARK exists),
- now also check LANDMARK using ismodule(). */
-
- /* Path: <argv0_path> / <VPATH macro> / Lib */
- /* or if VPATH is empty: <argv0_path> / Lib */
- if (safe_wcscpy(prefix, calculate->argv0_path, prefix_len) < 0) {
- return PATHLEN_ERR();
- }
-
- status = joinpath(prefix, calculate->vpath_macro, prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
-
- status = joinpath(prefix, L"Lib", prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
-
- int module;
- status = ismodule(prefix, &module);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- if (module) {
- /* BUILD_LANDMARK and LANDMARK found */
- *found = -1;
- return _PyStatus_OK();
- }
- }
-
- /* Search from argv0_path, until root is found */
- status = copy_absolute(prefix, calculate->argv0_path, prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
-
- do {
- /* Path: <argv0_path or substring> / <lib_python> / LANDMARK */
- size_t n = wcslen(prefix);
- status = joinpath(prefix, calculate->lib_python, prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
-
- int module;
- status = ismodule(prefix, &module);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- if (module) {
- *found = 1;
- return _PyStatus_OK();
- }
- prefix[n] = L'\0';
- reduce(prefix);
- } while (prefix[0]);
-
- /* Look at configure's PREFIX.
- Path: <PREFIX macro> / <lib_python> / LANDMARK */
- if (safe_wcscpy(prefix, calculate->prefix_macro, prefix_len) < 0) {
- return PATHLEN_ERR();
- }
- status = joinpath(prefix, calculate->lib_python, prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
-
- int module;
- status = ismodule(prefix, &module);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
+ PyObject *r = NULL;
+ PyObject *pathobj;
+ const wchar_t *path;
+ if (!PyArg_ParseTuple(args, "U", &pathobj)) {
+ return NULL;
}
- if (module) {
- *found = 1;
- return _PyStatus_OK();
+ path = PyUnicode_AsWideCharString(pathobj, NULL);
+ if (path) {
+#ifdef MS_WINDOWS
+ r = !(GetFileAttributesW(path) & FILE_ATTRIBUTE_DIRECTORY) ? Py_True : Py_False;
+#else
+ struct stat st;
+ r = (_Py_wstat(path, &st) == 0) && S_ISREG(st.st_mode) ? Py_True : Py_False;
+#endif
+ PyMem_Free((void *)path);
}
-
- /* Fail */
- *found = 0;
- return _PyStatus_OK();
+ Py_XINCREF(r);
+ return r;
}
-static PyStatus
-calculate_set_stdlib_dir(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
+static PyObject *
+getpath_isxfile(PyObject *Py_UNUSED(self), PyObject *args)
{
- // Note that, unlike calculate_set_prefix(), here we allow a negative
- // prefix_found. That means the source tree Lib dir gets used.
- if (!calculate->prefix_found) {
- return _PyStatus_OK();
- }
- PyStatus status;
- wchar_t *prefix = calculate->prefix;
- if (!_Py_isabs(prefix)) {
- prefix = _PyMem_RawWcsdup(prefix);
- if (prefix == NULL) {
- return _PyStatus_NO_MEMORY();
- }
- status = absolutize(&prefix);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- }
- wchar_t buf[MAXPATHLEN + 1];
- int res = _Py_normalize_path(prefix, buf, Py_ARRAY_LENGTH(buf));
- if (prefix != calculate->prefix) {
- PyMem_RawFree(prefix);
- }
- if (res < 0) {
- return PATHLEN_ERR();
+ PyObject *r = NULL;
+ PyObject *pathobj;
+ const wchar_t *path;
+ Py_ssize_t cchPath;
+ if (!PyArg_ParseTuple(args, "U", &pathobj)) {
+ return NULL;
}
- pathconfig->stdlib_dir = _PyMem_RawWcsdup(buf);
- if (pathconfig->stdlib_dir == NULL) {
- return _PyStatus_NO_MEMORY();
+ path = PyUnicode_AsWideCharString(pathobj, &cchPath);
+ if (path) {
+#ifdef MS_WINDOWS
+ const wchar_t *ext;
+ r = (GetFileAttributesW(path) & FILE_ATTRIBUTE_DIRECTORY) &&
+ SUCCEEDED(PathCchFindExtension(path, cchPath, &ext)) &&
+ (CompareStringOrdinal(ext, -1, L".exe", -1, 1 /* ignore case */) == CSTR_EQUAL)
+ ? Py_True : Py_False;
+#else
+ struct stat st;
+ r = (_Py_wstat(path, &st) == 0) &&
+ S_ISREG(st.st_mode) &&
+ (st.st_mode & 0111)
+ ? Py_True : Py_False;
+#endif
+ PyMem_Free((void *)path);
}
- return _PyStatus_OK();
+ Py_XINCREF(r);
+ return r;
}
-static PyStatus
-calculate_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
+static PyObject *
+getpath_joinpath(PyObject *Py_UNUSED(self), PyObject *args)
{
- wchar_t prefix[MAXPATHLEN+1];
- memset(prefix, 0, sizeof(prefix));
- size_t prefix_len = Py_ARRAY_LENGTH(prefix);
-
- PyStatus status;
- status = search_for_prefix(calculate, pathconfig,
- prefix, prefix_len,
- &calculate->prefix_found);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
-
- if (!calculate->prefix_found) {
- if (calculate->warnings) {
- fprintf(stderr,
- "Could not find platform independent libraries <prefix>\n");
- }
-
- calculate->prefix = joinpath2(calculate->prefix_macro,
- calculate->lib_python);
- }
- else {
- calculate->prefix = _PyMem_RawWcsdup(prefix);
- }
-
- if (calculate->prefix == NULL) {
- return _PyStatus_NO_MEMORY();
+ if (!PyTuple_Check(args)) {
+ PyErr_SetString(PyExc_TypeError, "requires tuple of arguments");
+ return NULL;
}
- return _PyStatus_OK();
-}
-
-
-static PyStatus
-calculate_set_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
-{
- /* Reduce prefix and exec_prefix to their essence,
- * e.g. /usr/local/lib/python1.5 is reduced to /usr/local.
- * If we're loading relative to the build directory,
- * return the compiled-in defaults instead.
- */
- if (calculate->prefix_found > 0) {
- wchar_t *prefix = _PyMem_RawWcsdup(calculate->prefix);
- if (prefix == NULL) {
- return _PyStatus_NO_MEMORY();
- }
-
- reduce(prefix);
- reduce(prefix);
- if (prefix[0]) {
- pathconfig->prefix = prefix;
- }
- else {
- PyMem_RawFree(prefix);
-
- /* The prefix is the root directory, but reduce() chopped
- off the "/". */
- pathconfig->prefix = _PyMem_RawWcsdup(separator);
- if (pathconfig->prefix == NULL) {
- return _PyStatus_NO_MEMORY();
+ Py_ssize_t n = PyTuple_GET_SIZE(args);
+ if (n == 0) {
+ return PyUnicode_FromString(NULL);
+ }
+ /* Convert all parts to wchar and accumulate max final length */
+ wchar_t **parts = (wchar_t **)PyMem_Malloc(n * sizeof(wchar_t *));
+ memset(parts, 0, n * sizeof(wchar_t *));
+ Py_ssize_t cchFinal = 0;
+ Py_ssize_t first = 0;
+
+ for (Py_ssize_t i = 0; i < n; ++i) {
+ PyObject *s = PyTuple_GET_ITEM(args, i);
+ Py_ssize_t cch;
+ if (s == Py_None) {
+ cch = 0;
+ } else if (PyUnicode_Check(s)) {
+ parts[i] = PyUnicode_AsWideCharString(s, &cch);
+ if (!parts[i]) {
+ cchFinal = -1;
+ break;
}
+ if (_Py_isabs(parts[i])) {
+ first = i;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError, "all arguments to joinpath() must be str or None");
+ cchFinal = -1;
+ break;
}
+ cchFinal += cch + 1;
}
- else {
- pathconfig->prefix = _PyMem_RawWcsdup(calculate->prefix_macro);
- if (pathconfig->prefix == NULL) {
- return _PyStatus_NO_MEMORY();
- }
- }
- return _PyStatus_OK();
-}
-
-
-static PyStatus
-calculate_pybuilddir(const wchar_t *argv0_path,
- wchar_t *exec_prefix, size_t exec_prefix_len,
- int *found)
-{
- PyStatus status;
-
- /* 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
- of shared library modules.
-
- Filename: <argv0_path> / "pybuilddir.txt" */
- wchar_t *filename = joinpath2(argv0_path, L"pybuilddir.txt");
- if (filename == NULL) {
- return _PyStatus_NO_MEMORY();
- }
-
- FILE *fp = _Py_wfopen(filename, L"rb");
- PyMem_RawFree(filename);
- if (fp == NULL) {
- errno = 0;
- return _PyStatus_OK();
- }
-
- char buf[MAXPATHLEN + 1];
- size_t n = fread(buf, 1, Py_ARRAY_LENGTH(buf) - 1, fp);
- buf[n] = '\0';
- fclose(fp);
-
- size_t dec_len;
- wchar_t *pybuilddir = _Py_DecodeUTF8_surrogateescape(buf, n, &dec_len);
- if (!pybuilddir) {
- return DECODE_LOCALE_ERR("pybuilddir.txt", dec_len);
- }
-
- /* Path: <argv0_path> / <pybuilddir content> */
- if (safe_wcscpy(exec_prefix, argv0_path, exec_prefix_len) < 0) {
- PyMem_RawFree(pybuilddir);
- return PATHLEN_ERR();
- }
- status = joinpath(exec_prefix, pybuilddir, exec_prefix_len);
- PyMem_RawFree(pybuilddir);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
-
- *found = -1;
- return _PyStatus_OK();
-}
-
-/* search_for_exec_prefix requires that argv0_path be no more than
- MAXPATHLEN bytes long.
-*/
-static PyStatus
-search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
- wchar_t *exec_prefix, size_t exec_prefix_len,
- int *found)
-{
- PyStatus status;
-
- /* If PYTHONHOME is set, we believe it unconditionally */
- if (pathconfig->home) {
- /* Path: <home> / <lib_python> / "lib-dynload" */
- wchar_t *delim = wcschr(pathconfig->home, DELIM);
- if (delim) {
- if (safe_wcscpy(exec_prefix, delim+1, exec_prefix_len) < 0) {
- return PATHLEN_ERR();
- }
+ wchar_t *final = cchFinal > 0 ? (wchar_t *)PyMem_Malloc(cchFinal * sizeof(wchar_t)) : NULL;
+ if (!final) {
+ for (Py_ssize_t i = 0; i < n; ++i) {
+ PyMem_Free(parts[i]);
}
- else {
- if (safe_wcscpy(exec_prefix, pathconfig->home, exec_prefix_len) < 0) {
- return PATHLEN_ERR();
- }
- }
- status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
+ PyMem_Free(parts);
+ if (cchFinal) {
+ PyErr_NoMemory();
+ return NULL;
}
- status = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- *found = 1;
- return _PyStatus_OK();
- }
-
- /* Check for pybuilddir.txt */
- assert(*found == 0);
- status = calculate_pybuilddir(calculate->argv0_path,
- exec_prefix, exec_prefix_len, found);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- if (*found) {
- return _PyStatus_OK();
+ return PyUnicode_FromStringAndSize(NULL, 0);
}
- /* Search from argv0_path, until root is found */
- status = copy_absolute(exec_prefix, calculate->argv0_path, exec_prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
-
- do {
- /* Path: <argv0_path or substring> / <lib_python> / "lib-dynload" */
- size_t n = wcslen(exec_prefix);
- status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- status = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
+ final[0] = '\0';
+ /* Now join all the paths. The final result should be shorter than the buffer */
+ for (Py_ssize_t i = 0; i < n; ++i) {
+ if (!parts[i]) {
+ continue;
}
- if (isdir(exec_prefix)) {
- *found = 1;
- return _PyStatus_OK();
+ if (i >= first && final) {
+ if (!final[0]) {
+ /* final is definitely long enough to fit any individual part */
+ wcscpy(final, parts[i]);
+ } else if (_Py_add_relfile(final, parts[i], cchFinal) < 0) {
+ /* if we fail, keep iterating to free memory, but stop adding parts */
+ PyMem_Free(final);
+ final = NULL;
+ }
}
- exec_prefix[n] = L'\0';
- reduce(exec_prefix);
- } while (exec_prefix[0]);
-
- /* Look at configure's EXEC_PREFIX.
-
- Path: <EXEC_PREFIX macro> / <lib_python> / "lib-dynload" */
- if (safe_wcscpy(exec_prefix, calculate->exec_prefix_macro, exec_prefix_len) < 0) {
- return PATHLEN_ERR();
+ PyMem_Free(parts[i]);
}
- status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- status = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- if (isdir(exec_prefix)) {
- *found = 1;
- return _PyStatus_OK();
+ PyMem_Free(parts);
+ if (!final) {
+ PyErr_SetString(PyExc_SystemError, "failed to join paths");
+ return NULL;
}
-
- /* Fail */
- *found = 0;
- return _PyStatus_OK();
+ PyObject *r = PyUnicode_FromWideChar(_Py_normpath(final, -1), -1);
+ PyMem_Free(final);
+ return r;
}
-static PyStatus
-calculate_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
+static PyObject *
+getpath_readlines(PyObject *Py_UNUSED(self), PyObject *args)
{
- PyStatus status;
- wchar_t exec_prefix[MAXPATHLEN+1];
- memset(exec_prefix, 0, sizeof(exec_prefix));
- size_t exec_prefix_len = Py_ARRAY_LENGTH(exec_prefix);
-
- status = search_for_exec_prefix(calculate, pathconfig,
- exec_prefix, exec_prefix_len,
- &calculate->exec_prefix_found);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
+ PyObject *r = NULL;
+ PyObject *pathobj;
+ const wchar_t *path;
+ if (!PyArg_ParseTuple(args, "U", &pathobj)) {
+ return NULL;
}
-
- if (!calculate->exec_prefix_found) {
- if (calculate->warnings) {
- fprintf(stderr,
- "Could not find platform dependent libraries <exec_prefix>\n");
- }
-
- /* <platlibdir> / "lib-dynload" */
- wchar_t *lib_dynload = joinpath2(calculate->platlibdir,
- L"lib-dynload");
- if (lib_dynload == NULL) {
- return _PyStatus_NO_MEMORY();
- }
-
- calculate->exec_prefix = joinpath2(calculate->exec_prefix_macro,
- lib_dynload);
- PyMem_RawFree(lib_dynload);
-
- if (calculate->exec_prefix == NULL) {
- return _PyStatus_NO_MEMORY();
- }
+ path = PyUnicode_AsWideCharString(pathobj, NULL);
+ if (!path) {
+ return NULL;
}
- else {
- /* If we found EXEC_PREFIX do *not* reduce it! (Yet.) */
- calculate->exec_prefix = _PyMem_RawWcsdup(exec_prefix);
- if (calculate->exec_prefix == NULL) {
- return _PyStatus_NO_MEMORY();
- }
+ FILE *fp = _Py_wfopen(path, L"rb");
+ PyMem_Free((void *)path);
+ if (!fp) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
}
- return _PyStatus_OK();
-}
-
-
-static PyStatus
-calculate_set_exec_prefix(PyCalculatePath *calculate,
- _PyPathConfig *pathconfig)
-{
- if (calculate->exec_prefix_found > 0) {
- wchar_t *exec_prefix = _PyMem_RawWcsdup(calculate->exec_prefix);
- if (exec_prefix == NULL) {
- return _PyStatus_NO_MEMORY();
- }
- reduce(exec_prefix);
- reduce(exec_prefix);
- reduce(exec_prefix);
-
- if (exec_prefix[0]) {
- pathconfig->exec_prefix = exec_prefix;
- }
- else {
- /* empty string: use SEP instead */
- PyMem_RawFree(exec_prefix);
-
- /* The exec_prefix is the root directory, but reduce() chopped
- off the "/". */
- pathconfig->exec_prefix = _PyMem_RawWcsdup(separator);
- if (pathconfig->exec_prefix == NULL) {
- return _PyStatus_NO_MEMORY();
- }
- }
+ r = PyList_New(0);
+ if (!r) {
+ fclose(fp);
+ return NULL;
}
- else {
- pathconfig->exec_prefix = _PyMem_RawWcsdup(calculate->exec_prefix_macro);
- if (pathconfig->exec_prefix == NULL) {
- return _PyStatus_NO_MEMORY();
- }
+ const size_t MAX_FILE = 32 * 1024;
+ char *buffer = (char *)PyMem_Malloc(MAX_FILE);
+ if (!buffer) {
+ Py_DECREF(r);
+ fclose(fp);
+ return NULL;
}
- return _PyStatus_OK();
-}
-
-
-/* Similar to shutil.which().
- If found, write the path into *abs_path_p. */
-static PyStatus
-calculate_which(const wchar_t *path_env, wchar_t *program_name,
- wchar_t **abs_path_p)
-{
- while (1) {
- wchar_t *delim = wcschr(path_env, DELIM);
- wchar_t *abs_path;
-
- if (delim) {
- wchar_t *path = substring(path_env, delim - path_env);
- if (path == NULL) {
- return _PyStatus_NO_MEMORY();
- }
- abs_path = joinpath2(path, program_name);
- PyMem_RawFree(path);
- }
- else {
- abs_path = joinpath2(path_env, program_name);
- }
-
- if (abs_path == NULL) {
- return _PyStatus_NO_MEMORY();
- }
-
- if (isxfile(abs_path)) {
- *abs_path_p = abs_path;
- return _PyStatus_OK();
- }
- PyMem_RawFree(abs_path);
- if (!delim) {
- break;
- }
- path_env = delim + 1;
+ size_t cb = fread(buffer, 1, MAX_FILE, fp);
+ fclose(fp);
+ if (!cb) {
+ return r;
}
-
- /* not found */
- return _PyStatus_OK();
-}
-
-
-#ifdef __APPLE__
-static PyStatus
-calculate_program_macos(wchar_t **abs_path_p)
-{
- char execpath[MAXPATHLEN + 1];
- uint32_t nsexeclength = Py_ARRAY_LENGTH(execpath) - 1;
-
- /* On Mac OS X, if a script uses an interpreter of the form
- "#!/opt/python2.3/bin/python", the kernel only passes "python"
- as argv[0], which falls through to the $PATH search below.
- If /opt/python2.3/bin isn't in your path, or is near the end,
- this algorithm may incorrectly find /usr/bin/python. To work
- around this, we can use _NSGetExecutablePath to get a better
- hint of what the intended interpreter was, although this
- will fail if a relative path was used. but in that case,
- absolutize() should help us out below
- */
- if (_NSGetExecutablePath(execpath, &nsexeclength) != 0
- || (wchar_t)execpath[0] != SEP)
- {
- /* _NSGetExecutablePath() failed or the path is relative */
- return _PyStatus_OK();
+ if (cb >= MAX_FILE) {
+ Py_DECREF(r);
+ PyErr_SetString(PyExc_MemoryError,
+ "cannot read file larger than 32KB during initialization");
+ return NULL;
}
+ buffer[cb] = '\0';
size_t len;
- *abs_path_p = Py_DecodeLocale(execpath, &len);
- if (*abs_path_p == NULL) {
- return DECODE_LOCALE_ERR("executable path", len);
- }
- return _PyStatus_OK();
-}
-#endif /* __APPLE__ */
-
-
-static PyStatus
-calculate_program_impl(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
-{
- assert(pathconfig->program_full_path == NULL);
-
- PyStatus status;
-
- /* If there is no slash in the argv0 path, then we have to
- * assume python is on the user's $PATH, since there's no
- * other way to find a directory to start the search from. If
- * $PATH isn't exported, you lose.
- */
- if (wcschr(pathconfig->program_name, SEP)) {
- pathconfig->program_full_path = _PyMem_RawWcsdup(pathconfig->program_name);
- if (pathconfig->program_full_path == NULL) {
- return _PyStatus_NO_MEMORY();
- }
- return _PyStatus_OK();
- }
-
-#ifdef __APPLE__
- wchar_t *abs_path = NULL;
- status = calculate_program_macos(&abs_path);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- if (abs_path) {
- pathconfig->program_full_path = abs_path;
- return _PyStatus_OK();
+ wchar_t *wbuffer = _Py_DecodeUTF8_surrogateescape(buffer, cb, &len);
+ PyMem_Free((void *)buffer);
+ if (!wbuffer) {
+ Py_DECREF(r);
+ PyErr_NoMemory();
+ return NULL;
}
-#endif /* __APPLE__ */
- if (calculate->path_env) {
- wchar_t *abs_path = NULL;
- status = calculate_which(calculate->path_env, pathconfig->program_name,
- &abs_path);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
+ wchar_t *p1 = wbuffer;
+ wchar_t *p2 = p1;
+ while ((p2 = wcschr(p1, L'\n')) != NULL) {
+ size_t cb = p2 - p1;
+ while (cb && (p1[cb] == L'\n' || p1[cb] == L'\r')) {
+ --cb;
}
- if (abs_path) {
- pathconfig->program_full_path = abs_path;
- return _PyStatus_OK();
+ PyObject *u = PyUnicode_FromWideChar(p1, cb + 1);
+ if (!u || PyList_Append(r, u) < 0) {
+ Py_XDECREF(u);
+ Py_CLEAR(r);
+ break;
}
+ Py_DECREF(u);
+ p1 = p2 + 1;
}
-
- /* In the last resort, use an empty string */
- pathconfig->program_full_path = _PyMem_RawWcsdup(L"");
- if (pathconfig->program_full_path == NULL) {
- return _PyStatus_NO_MEMORY();
+ if (r && p1 && *p1) {
+ PyObject *u = PyUnicode_FromWideChar(p1, -1);
+ if (!u || PyList_Append(r, u) < 0) {
+ Py_CLEAR(r);
+ }
+ Py_XDECREF(u);
}
- return _PyStatus_OK();
+ PyMem_RawFree(wbuffer);
+ return r;
}
-/* Calculate pathconfig->program_full_path */
-static PyStatus
-calculate_program(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
+static PyObject *
+getpath_realpath(PyObject *Py_UNUSED(self) , PyObject *args)
{
- PyStatus status;
-
- status = calculate_program_impl(calculate, pathconfig);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
+ PyObject *pathobj;
+ if (!PyArg_ParseTuple(args, "U", &pathobj)) {
+ return NULL;
}
-
- if (pathconfig->program_full_path[0] != '\0') {
- /* program_full_path is not empty */
-
- /* Make sure that program_full_path is an absolute path */
- if (!_Py_isabs(pathconfig->program_full_path)) {
- status = absolutize(&pathconfig->program_full_path);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- }
-
-#if defined(__CYGWIN__) || defined(__MINGW32__)
- /* For these platforms it is necessary to ensure that the .exe suffix
- * is appended to the filename, otherwise there is potential for
- * sys.executable to return the name of a directory under the same
- * path (bpo-28441).
- */
- status = add_exe_suffix(&pathconfig->program_full_path);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
-#endif
+#if defined(HAVE_READLINK)
+ /* This readlink calculation only resolves a symlinked file, and
+ does not resolve any path segments. This is consistent with
+ prior releases, however, the realpath implementation below is
+ potentially correct in more cases. */
+ PyObject *r = NULL;
+ int nlink = 0;
+ wchar_t *path = PyUnicode_AsWideCharString(pathobj, NULL);
+ if (!path) {
+ goto done;
}
- return _PyStatus_OK();
-}
-
-
-#if HAVE_READLINK
-static PyStatus
-resolve_symlinks(wchar_t **path_p)
-{
- wchar_t new_path[MAXPATHLEN + 1];
- const size_t new_path_len = Py_ARRAY_LENGTH(new_path);
- unsigned int nlink = 0;
-
- while (1) {
- int linklen = _Py_wreadlink(*path_p, new_path, new_path_len);
+ wchar_t *path2 = _PyMem_RawWcsdup(path);
+ PyMem_Free((void *)path);
+ path = path2;
+ while (path) {
+ wchar_t resolved[MAXPATHLEN + 1];
+ int linklen = _Py_wreadlink(path, resolved, Py_ARRAY_LENGTH(resolved));
if (linklen == -1) {
- /* not a symbolic link: we are done */
+ r = PyUnicode_FromWideChar(path, -1);
break;
}
-
- if (_Py_isabs(new_path)) {
- PyMem_RawFree(*path_p);
- *path_p = _PyMem_RawWcsdup(new_path);
- if (*path_p == NULL) {
- return _PyStatus_NO_MEMORY();
+ if (_Py_isabs(resolved)) {
+ PyMem_RawFree((void *)path);
+ path = _PyMem_RawWcsdup(resolved);
+ } else {
+ wchar_t *s = wcsrchr(path, SEP);
+ if (s) {
+ *s = L'\0';
}
+ path2 = _Py_normpath(_Py_join_relfile(path, resolved), -1);
+ PyMem_RawFree((void *)path);
+ path = path2;
}
- else {
- /* new_path is relative to path */
- reduce(*path_p);
-
- wchar_t *abs_path = joinpath2(*path_p, new_path);
- if (abs_path == NULL) {
- return _PyStatus_NO_MEMORY();
- }
-
- PyMem_RawFree(*path_p);
- *path_p = abs_path;
- }
-
nlink++;
/* 40 is the Linux kernel 4.2 limit */
if (nlink >= 40) {
- return _PyStatus_ERR("maximum number of symbolic links reached");
+ PyErr_SetString(PyExc_OSError, "maximum number of symbolic links reached");
+ break;
}
}
- return _PyStatus_OK();
-}
-#endif /* HAVE_READLINK */
-
-
-#ifdef WITH_NEXT_FRAMEWORK
-static PyStatus
-calculate_argv0_path_framework(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
-{
- NSModule pythonModule;
-
- /* On Mac OS X we have a special case if we're running from a framework.
- This is because the python home should be set relative to the library,
- which is in the framework, not relative to the executable, which may
- be outside of the framework. Except when we're in the build
- directory... */
- pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_Initialize"));
-
- /* Use dylib functions to find out where the framework was loaded from */
- const char* modPath = NSLibraryNameForModule(pythonModule);
- if (modPath == NULL) {
- return _PyStatus_OK();
- }
-
- /* We're in a framework.
- See if we might be in the build directory. The framework in the
- build directory is incomplete, it only has the .dylib and a few
- needed symlinks, it doesn't have the Lib directories and such.
- If we're running with the framework from the build directory we must
- be running the interpreter in the build directory, so we use the
- build-directory-specific logic to find Lib and such. */
- size_t len;
- wchar_t* wbuf = Py_DecodeLocale(modPath, &len);
- if (wbuf == NULL) {
- return DECODE_LOCALE_ERR("framework location", len);
+ if (!path) {
+ PyErr_NoMemory();
}
-
- /* Path: reduce(modPath) / lib_python / LANDMARK */
- PyStatus status;
-
- wchar_t *parent = _PyMem_RawWcsdup(wbuf);
- if (parent == NULL) {
- status = _PyStatus_NO_MEMORY();
+done:
+ PyMem_RawFree((void *)path);
+ return r;
+
+#elif defined(HAVE_REALPATH)
+ PyObject *r = NULL;
+ struct stat st;
+ const char *narrow = NULL;
+ wchar_t *path = PyUnicode_AsWideCharString(pathobj, NULL);
+ if (!path) {
goto done;
}
-
- reduce(parent);
- wchar_t *lib_python = joinpath2(parent, calculate->lib_python);
- PyMem_RawFree(parent);
-
- if (lib_python == NULL) {
- status = _PyStatus_NO_MEMORY();
+ narrow = Py_EncodeLocale(path, NULL);
+ if (!narrow) {
+ PyErr_NoMemory();
goto done;
}
-
- int module;
- status = ismodule(lib_python, &module);
- PyMem_RawFree(lib_python);
-
- if (_PyStatus_EXCEPTION(status)) {
+ if (lstat(narrow, &st)) {
+ PyErr_SetFromErrno(PyExc_OSError);
goto done;
}
- if (!module) {
- /* We are in the build directory so use the name of the
- executable - we know that the absolute path is passed */
- PyMem_RawFree(calculate->argv0_path);
- calculate->argv0_path = _PyMem_RawWcsdup(pathconfig->program_full_path);
- if (calculate->argv0_path == NULL) {
- status = _PyStatus_NO_MEMORY();
- goto done;
- }
-
- status = _PyStatus_OK();
+ if (!S_ISLNK(st.st_mode)) {
+ Py_INCREF(pathobj);
+ r = pathobj;
goto done;
}
-
- /* Use the location of the library as argv0_path */
- PyMem_RawFree(calculate->argv0_path);
- calculate->argv0_path = wbuf;
- return _PyStatus_OK();
-
+ wchar_t resolved[MAXPATHLEN+1];
+ if (_Py_wrealpath(path, resolved, MAXPATHLEN) == NULL) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ } else {
+ r = PyUnicode_FromWideChar(resolved, -1);
+ }
done:
- PyMem_RawFree(wbuf);
- return status;
-}
+ PyMem_Free((void *)path);
+ PyMem_Free((void *)narrow);
+ return r;
#endif
+ Py_INCREF(pathobj);
+ return pathobj;
+}
-static PyStatus
-calculate_argv0_path(PyCalculatePath *calculate,
- _PyPathConfig *pathconfig)
-{
- PyStatus status;
-
- calculate->argv0_path = _PyMem_RawWcsdup(pathconfig->program_full_path);
- if (calculate->argv0_path == NULL) {
- return _PyStatus_NO_MEMORY();
- }
-
-#ifdef WITH_NEXT_FRAMEWORK
- status = calculate_argv0_path_framework(calculate, pathconfig);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
-#endif
-
- status = resolve_symlinks(&calculate->argv0_path);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- reduce(calculate->argv0_path);
+static PyMethodDef getpath_methods[] = {
+ {"abspath", getpath_abspath, METH_VARARGS, NULL},
+ {"basename", getpath_basename, METH_VARARGS, NULL},
+ {"dirname", getpath_dirname, METH_VARARGS, NULL},
+ {"hassuffix", getpath_hassuffix, METH_VARARGS, NULL},
+ {"isabs", getpath_isabs, METH_VARARGS, NULL},
+ {"isdir", getpath_isdir, METH_VARARGS, NULL},
+ {"isfile", getpath_isfile, METH_VARARGS, NULL},
+ {"isxfile", getpath_isxfile, METH_VARARGS, NULL},
+ {"joinpath", getpath_joinpath, METH_VARARGS, NULL},
+ {"readlines", getpath_readlines, METH_VARARGS, NULL},
+ {"realpath", getpath_realpath, METH_VARARGS, NULL},
+ {NULL, NULL, 0, NULL}
+};
- return _PyStatus_OK();
-}
+/* Two implementations of warn() to use depending on whether warnings
+ are enabled or not. */
-static PyStatus
-calculate_open_pyenv(PyCalculatePath *calculate, FILE **env_file_p)
+static PyObject *
+getpath_warn(PyObject *Py_UNUSED(self), PyObject *args)
{
- *env_file_p = NULL;
-
- const wchar_t *env_cfg = L"pyvenv.cfg";
-
- /* Filename: <argv0_path> / "pyvenv.cfg" */
- wchar_t *filename = joinpath2(calculate->argv0_path, env_cfg);
- if (filename == NULL) {
- return _PyStatus_NO_MEMORY();
+ PyObject *msgobj;
+ if (!PyArg_ParseTuple(args, "U", &msgobj)) {
+ return NULL;
}
+ fprintf(stderr, "%s\n", PyUnicode_AsUTF8(msgobj));
+ Py_RETURN_NONE;
+}
- *env_file_p = _Py_wfopen(filename, L"r");
- PyMem_RawFree(filename);
- if (*env_file_p != NULL) {
- return _PyStatus_OK();
+static PyObject *
+getpath_nowarn(PyObject *Py_UNUSED(self), PyObject *args)
+{
+ Py_RETURN_NONE;
+}
- }
- /* fopen() failed: reset errno */
- errno = 0;
+static PyMethodDef getpath_warn_method = {"warn", getpath_warn, METH_VARARGS, NULL};
+static PyMethodDef getpath_nowarn_method = {"warn", getpath_nowarn, METH_VARARGS, NULL};
- /* Path: <basename(argv0_path)> / "pyvenv.cfg" */
- wchar_t *parent = _PyMem_RawWcsdup(calculate->argv0_path);
- if (parent == NULL) {
- return _PyStatus_NO_MEMORY();
+/* Add the helper functions to the dict */
+static int
+funcs_to_dict(PyObject *dict, int warnings)
+{
+ for (PyMethodDef *m = getpath_methods; m->ml_name; ++m) {
+ PyObject *f = PyCFunction_NewEx(m, NULL, NULL);
+ if (!f) {
+ return 0;
+ }
+ if (PyDict_SetItemString(dict, m->ml_name, f) < 0) {
+ Py_DECREF(f);
+ return 0;
+ }
+ Py_DECREF(f);
}
- reduce(parent);
-
- filename = joinpath2(parent, env_cfg);
- PyMem_RawFree(parent);
- if (filename == NULL) {
- return _PyStatus_NO_MEMORY();
+ PyMethodDef *m2 = warnings ? &getpath_warn_method : &getpath_nowarn_method;
+ PyObject *f = PyCFunction_NewEx(m2, NULL, NULL);
+ if (!f) {
+ return 0;
}
-
- *env_file_p = _Py_wfopen(filename, L"r");
- PyMem_RawFree(filename);
-
- if (*env_file_p == NULL) {
- /* fopen() failed: reset errno */
- errno = 0;
+ if (PyDict_SetItemString(dict, m2->ml_name, f) < 0) {
+ Py_DECREF(f);
+ return 0;
}
- return _PyStatus_OK();
+ Py_DECREF(f);
+ return 1;
}
-/* Search for an "pyvenv.cfg" environment configuration file, first in the
- executable's directory and then in the parent directory.
- If found, open it for use when searching for prefixes.
-
- Write the 'home' variable of pyvenv.cfg into calculate->argv0_path. */
-static PyStatus
-calculate_read_pyenv(PyCalculatePath *calculate)
+/* Add a wide-character string constant to the dict */
+static int
+wchar_to_dict(PyObject *dict, const char *key, const wchar_t *s)
{
- PyStatus status;
- FILE *env_file = NULL;
-
- status = calculate_open_pyenv(calculate, &env_file);
- if (_PyStatus_EXCEPTION(status)) {
- assert(env_file == NULL);
- return status;
- }
- if (env_file == NULL) {
- /* pyvenv.cfg not found */
- return _PyStatus_OK();
- }
-
- /* Look for a 'home' variable and set argv0_path to it, if found */
- wchar_t *home = NULL;
- status = _Py_FindEnvConfigValue(env_file, L"home", &home);
- if (_PyStatus_EXCEPTION(status)) {
- fclose(env_file);
- return status;
- }
-
- if (home) {
- PyMem_RawFree(calculate->argv0_path);
- calculate->argv0_path = home;
+ PyObject *u;
+ int r;
+ if (s && s[0]) {
+ u = PyUnicode_FromWideChar(s, -1);
+ if (!u) {
+ return 0;
+ }
+ } else {
+ u = Py_None;
+ Py_INCREF(u);
}
- fclose(env_file);
- return _PyStatus_OK();
+ r = PyDict_SetItemString(dict, key, u) == 0;
+ Py_DECREF(u);
+ return r;
}
-static PyStatus
-calculate_zip_path(PyCalculatePath *calculate)
+/* Add a narrow string constant to the dict, using default locale decoding */
+static int
+decode_to_dict(PyObject *dict, const char *key, const char *s)
{
- PyStatus res;
-
- /* Path: <platlibdir> / "pythonXY.zip" */
- wchar_t *path = joinpath2(calculate->platlibdir,
- L"python" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION)
- L".zip");
- if (path == NULL) {
- return _PyStatus_NO_MEMORY();
+ PyObject *u = NULL;
+ int r;
+ if (s && s[0]) {
+ size_t len;
+ const wchar_t *w = Py_DecodeLocale(s, &len);
+ if (w) {
+ u = PyUnicode_FromWideChar(w, len);
+ PyMem_RawFree((void *)w);
+ }
+ if (!u) {
+ return 0;
+ }
+ } else {
+ u = Py_None;
+ Py_INCREF(u);
}
+ r = PyDict_SetItemString(dict, key, u) == 0;
+ Py_DECREF(u);
+ return r;
+}
- if (calculate->prefix_found > 0) {
- /* Use the reduced prefix returned by Py_GetPrefix()
-
- Path: <basename(basename(prefix))> / <platlibdir> / "pythonXY.zip" */
- wchar_t *parent = _PyMem_RawWcsdup(calculate->prefix);
- if (parent == NULL) {
- res = _PyStatus_NO_MEMORY();
- goto done;
+/* Add an environment variable to the dict, optionally clearing it afterwards */
+static int
+env_to_dict(PyObject *dict, const char *key, int and_clear)
+{
+ PyObject *u = NULL;
+ int r = 0;
+ assert(strncmp(key, "ENV_", 4) == 0);
+ assert(strlen(key) < 64);
+#ifdef MS_WINDOWS
+ wchar_t wkey[64];
+ // Quick convert to wchar_t, since we know key is ASCII
+ wchar_t *wp = wkey;
+ for (const char *p = &key[4]; *p; ++p) {
+ assert(*p < 128);
+ *wp++ = *p;
+ }
+ *wp = L'\0';
+ const wchar_t *v = _wgetenv(wkey);
+ if (v) {
+ u = PyUnicode_FromWideChar(v, -1);
+ if (!u) {
+ PyErr_Clear();
+ }
+ }
+#else
+ const char *v = getenv(&key[4]);
+ if (v) {
+ size_t len;
+ const wchar_t *w = Py_DecodeLocale(v, &len);
+ if (w) {
+ u = PyUnicode_FromWideChar(w, len);
+ if (!u) {
+ PyErr_Clear();
+ }
+ PyMem_RawFree((void *)w);
}
- reduce(parent);
- reduce(parent);
- calculate->zip_path = joinpath2(parent, path);
- PyMem_RawFree(parent);
}
- else {
- calculate->zip_path = joinpath2(calculate->prefix_macro, path);
- }
-
- if (calculate->zip_path == NULL) {
- res = _PyStatus_NO_MEMORY();
- goto done;
+#endif
+ if (u) {
+ r = PyDict_SetItemString(dict, key, u) == 0;
+ Py_DECREF(u);
+ } else {
+ r = PyDict_SetItemString(dict, key, Py_None) == 0;
+ }
+ if (r && and_clear) {
+#ifdef MS_WINDOWS
+ _wputenv_s(wkey, L"");
+#else
+ unsetenv(&key[4]);
+#endif
}
-
- res = _PyStatus_OK();
-
-done:
- PyMem_RawFree(path);
- return res;
+ return r;
}
-static PyStatus
-calculate_module_search_path(PyCalculatePath *calculate,
- _PyPathConfig *pathconfig)
+/* Add an integer constant to the dict */
+static int
+int_to_dict(PyObject *dict, const char *key, int v)
{
- /* Calculate size of return buffer */
- size_t bufsz = 0;
- if (calculate->pythonpath_env != NULL) {
- bufsz += wcslen(calculate->pythonpath_env) + 1;
+ PyObject *o;
+ int r;
+ o = PyLong_FromLong(v);
+ if (!o) {
+ return 0;
}
+ r = PyDict_SetItemString(dict, key, o) == 0;
+ Py_DECREF(o);
+ return r;
+}
- wchar_t *defpath = calculate->pythonpath_macro;
- size_t prefixsz = wcslen(calculate->prefix) + 1;
- while (1) {
- wchar_t *delim = wcschr(defpath, DELIM);
-
- if (!_Py_isabs(defpath)) {
- /* Paths are relative to prefix */
- bufsz += prefixsz;
- }
- if (delim) {
- bufsz += delim - defpath + 1;
- }
- else {
- bufsz += wcslen(defpath) + 1;
- break;
+#ifdef MS_WINDOWS
+static int
+winmodule_to_dict(PyObject *dict, const char *key, HMODULE mod)
+{
+ wchar_t *buffer = NULL;
+ for (DWORD cch = 256; buffer == NULL && cch < (1024 * 1024); cch *= 2) {
+ buffer = (wchar_t*)PyMem_RawMalloc(cch * sizeof(wchar_t));
+ if (buffer) {
+ if (GetModuleFileNameW(mod, buffer, cch) == cch) {
+ PyMem_RawFree(buffer);
+ buffer = NULL;
+ }
}
- defpath = delim + 1;
- }
-
- bufsz += wcslen(calculate->zip_path) + 1;
- bufsz += wcslen(calculate->exec_prefix) + 1;
-
- /* Allocate the buffer */
- wchar_t *buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t));
- if (buf == NULL) {
- return _PyStatus_NO_MEMORY();
}
- buf[0] = '\0';
-
- /* Run-time value of $PYTHONPATH goes first */
- if (calculate->pythonpath_env) {
- wcscpy(buf, calculate->pythonpath_env);
- wcscat(buf, delimiter);
- }
-
- /* Next is the default zip path */
- wcscat(buf, calculate->zip_path);
- wcscat(buf, delimiter);
-
- /* Next goes merge of compile-time $PYTHONPATH with
- * dynamically located prefix.
- */
- defpath = calculate->pythonpath_macro;
- while (1) {
- wchar_t *delim = wcschr(defpath, DELIM);
+ int r = wchar_to_dict(dict, key, buffer);
+ PyMem_RawFree(buffer);
+ return r;
+}
+#endif
- if (!_Py_isabs(defpath)) {
- wcscat(buf, calculate->prefix);
- if (prefixsz >= 2 && calculate->prefix[prefixsz - 2] != SEP &&
- defpath[0] != (delim ? DELIM : L'\0'))
- {
- /* not empty */
- wcscat(buf, separator);
- }
- }
- if (delim) {
- size_t len = delim - defpath + 1;
- size_t end = wcslen(buf) + len;
- wcsncat(buf, defpath, len);
- buf[end] = '\0';
+/* Add the current executable's path to the dict */
+static int
+progname_to_dict(PyObject *dict, const char *key)
+{
+#ifdef MS_WINDOWS
+ return winmodule_to_dict(dict, key, NULL);
+#elif defined(__APPLE__)
+ char *path;
+ uint32_t pathLen = 256;
+ while (pathLen) {
+ path = PyMem_RawMalloc((pathLen + 1) * sizeof(char));
+ if (!path) {
+ return 0;
+ }
+ if (_NSGetExecutablePath(path, &pathLen) != 0) {
+ PyMem_RawFree(path);
+ continue;
}
- else {
- wcscat(buf, defpath);
- break;
+ // Only keep if the path is absolute
+ if (path[0] == SEP) {
+ int r = decode_to_dict(dict, key, path);
+ PyMem_RawFree(path);
+ return r;
}
- defpath = delim + 1;
+ // Fall back and store None
+ PyMem_RawFree(path);
+ break;
}
- wcscat(buf, delimiter);
-
- /* Finally, on goes the directory for dynamic-load modules */
- wcscat(buf, calculate->exec_prefix);
-
- pathconfig->module_search_path = buf;
- return _PyStatus_OK();
+#endif
+ return PyDict_SetItemString(dict, key, Py_None) == 0;
}
-static PyStatus
-calculate_init(PyCalculatePath *calculate, const PyConfig *config)
+/* Add the runtime library's path to the dict */
+static int
+library_to_dict(PyObject *dict, const char *key)
{
- size_t len;
-
- calculate->warnings = config->pathconfig_warnings;
- calculate->pythonpath_env = config->pythonpath_env;
- calculate->platlibdir = config->platlibdir;
-
- const char *path = getenv("PATH");
- if (path) {
- calculate->path_env = Py_DecodeLocale(path, &len);
- if (!calculate->path_env) {
- return DECODE_LOCALE_ERR("PATH environment variable", len);
- }
+#ifdef MS_WINDOWS
+ extern HMODULE PyWin_DLLhModule;
+ if (PyWin_DLLhModule) {
+ return winmodule_to_dict(dict, key, PyWin_DLLhModule);
}
+#elif defined(WITH_NEXT_FRAMEWORK)
+ static const char modPath[MAXPATHLEN + 1];
+ static int modPathInitialized = -1;
+ if (modPathInitialized < 0) {
+ NSModule pythonModule;
+ modPathInitialized = 0;
- /* Decode macros */
- calculate->pythonpath_macro = Py_DecodeLocale(PYTHONPATH, &len);
- if (!calculate->pythonpath_macro) {
- return DECODE_LOCALE_ERR("PYTHONPATH macro", len);
- }
- calculate->prefix_macro = Py_DecodeLocale(PREFIX, &len);
- if (!calculate->prefix_macro) {
- return DECODE_LOCALE_ERR("PREFIX macro", len);
- }
- calculate->exec_prefix_macro = Py_DecodeLocale(EXEC_PREFIX, &len);
- if (!calculate->exec_prefix_macro) {
- return DECODE_LOCALE_ERR("EXEC_PREFIX macro", len);
- }
- calculate->vpath_macro = Py_DecodeLocale(VPATH, &len);
- if (!calculate->vpath_macro) {
- return DECODE_LOCALE_ERR("VPATH macro", len);
- }
+ /* On Mac OS X we have a special case if we're running from a framework.
+ This is because the python home should be set relative to the library,
+ which is in the framework, not relative to the executable, which may
+ be outside of the framework. Except when we're in the build
+ directory... */
+ pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_Initialize"));
- // <platlibdir> / "pythonX.Y"
- wchar_t *pyversion = Py_DecodeLocale("python" VERSION, &len);
- if (!pyversion) {
- return DECODE_LOCALE_ERR("VERSION macro", len);
+ /* Use dylib functions to find out where the framework was loaded from */
+ const char *path = NSLibraryNameForModule(pythonModule);
+ if (path) {
+ strncpy(modPath, path, MAXPATHLEN);
+ }
}
- calculate->lib_python = joinpath2(config->platlibdir, pyversion);
- PyMem_RawFree(pyversion);
- if (calculate->lib_python == NULL) {
- return _PyStatus_NO_MEMORY();
+ if (modPathInitialized > 0) {
+ return decode_to_dict(dict, key, modPath);
}
-
- return _PyStatus_OK();
+#endif
+ return PyDict_SetItemString(dict, key, Py_None) == 0;
}
-static void
-calculate_free(PyCalculatePath *calculate)
+PyObject *
+_Py_Get_Getpath_CodeObject()
{
- PyMem_RawFree(calculate->pythonpath_macro);
- PyMem_RawFree(calculate->prefix_macro);
- PyMem_RawFree(calculate->exec_prefix_macro);
- PyMem_RawFree(calculate->vpath_macro);
- PyMem_RawFree(calculate->lib_python);
- PyMem_RawFree(calculate->path_env);
- PyMem_RawFree(calculate->zip_path);
- PyMem_RawFree(calculate->argv0_path);
- PyMem_RawFree(calculate->prefix);
- PyMem_RawFree(calculate->exec_prefix);
+ return PyMarshal_ReadObjectFromString(
+ (const char*)_Py_M__getpath, sizeof(_Py_M__getpath));
}
-static PyStatus
-calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
-{
- PyStatus status;
-
- if (pathconfig->program_full_path == NULL) {
- status = calculate_program(calculate, pathconfig);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
- }
+/* Perform the actual path calculation.
- status = calculate_argv0_path(calculate, pathconfig);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
+ When compute_path_config is 0, this only reads any initialised path
+ config values into the PyConfig struct. For example, Py_SetHome() or
+ Py_SetPath(). The only error should be due to failed memory allocation.
- /* If a pyvenv.cfg configure file is found,
- argv0_path is overridden with its 'home' variable. */
- status = calculate_read_pyenv(calculate);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
+ When compute_path_config is 1, full path calculation is performed.
+ The GIL must be held, and there may be filesystem access, side
+ effects, and potential unraisable errors that are reported directly
+ to stderr.
- status = calculate_prefix(calculate, pathconfig);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
+ Calling this function multiple times on the same PyConfig is only
+ safe because already-configured values are not recalculated. To
+ actually recalculate paths, you need a clean PyConfig.
+*/
+PyStatus
+_PyConfig_InitPathConfig(PyConfig *config, int compute_path_config)
+{
+ PyStatus status = _PyPathConfig_ReadGlobal(config);
- status = calculate_zip_path(calculate);
- if (_PyStatus_EXCEPTION(status)) {
+ if (_PyStatus_EXCEPTION(status) || !compute_path_config) {
return status;
}
- status = calculate_exec_prefix(calculate, pathconfig);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
+ if (!_PyThreadState_UncheckedGet()) {
+ return PyStatus_Error("cannot calculate path configuration without GIL");
}
- if ((!calculate->prefix_found || !calculate->exec_prefix_found)
- && calculate->warnings)
- {
- fprintf(stderr,
- "Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]\n");
+ PyObject *configDict = _PyConfig_AsDict(config);
+ if (!configDict) {
+ PyErr_Clear();
+ return PyStatus_NoMemory();
}
- if (pathconfig->module_search_path == NULL) {
- status = calculate_module_search_path(calculate, pathconfig);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
+ PyObject *dict = PyDict_New();
+ if (!dict) {
+ PyErr_Clear();
+ Py_DECREF(configDict);
+ return PyStatus_NoMemory();
}
- if (pathconfig->stdlib_dir == NULL) {
- /* This must be done *before* calculate_set_prefix() is called. */
- status = calculate_set_stdlib_dir(calculate, pathconfig);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
+ if (PyDict_SetItemString(dict, "config", configDict) < 0) {
+ PyErr_Clear();
+ Py_DECREF(configDict);
+ Py_DECREF(dict);
+ return PyStatus_NoMemory();
}
+ /* reference now held by dict */
+ Py_DECREF(configDict);
- if (pathconfig->prefix == NULL) {
- status = calculate_set_prefix(calculate, pathconfig);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
+ PyObject *co = _Py_Get_Getpath_CodeObject();
+ if (!co || !PyCode_Check(co)) {
+ PyErr_Clear();
+ Py_XDECREF(co);
+ Py_DECREF(dict);
+ return PyStatus_Error("error reading frozen getpath.py");
}
- if (pathconfig->exec_prefix == NULL) {
- status = calculate_set_exec_prefix(calculate, pathconfig);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
+#ifdef MS_WINDOWS
+ PyObject *winreg = PyImport_ImportModule("winreg");
+ if (!winreg || PyDict_SetItemString(dict, "winreg", winreg) < 0) {
+ PyErr_Clear();
+ Py_XDECREF(winreg);
+ if (PyDict_SetItemString(dict, "winreg", Py_None) < 0) {
+ PyErr_Clear();
+ Py_DECREF(co);
+ Py_DECREF(dict);
+ return PyStatus_Error("error importing winreg module");
}
+ } else {
+ Py_DECREF(winreg);
}
- return _PyStatus_OK();
-}
-
-
-/* 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
- - module_search_path
- - prefix
- - exec_prefix
-
- If a field is already set (non NULL), it is left unchanged. */
-PyStatus
-_PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
-{
- PyStatus status;
- PyCalculatePath calculate;
- memset(&calculate, 0, sizeof(calculate));
+#endif
- status = calculate_init(&calculate, config);
- if (_PyStatus_EXCEPTION(status)) {
- goto done;
- }
+ if (
+#ifdef MS_WINDOWS
+ !decode_to_dict(dict, "os_name", "nt") ||
+#elif defined(__APPLE__)
+ !decode_to_dict(dict, "os_name", "darwin") ||
+#else
+ !decode_to_dict(dict, "os_name", "posix") ||
+#endif
+ !decode_to_dict(dict, "PREFIX", PREFIX) ||
+ !decode_to_dict(dict, "EXEC_PREFIX", EXEC_PREFIX) ||
+ !decode_to_dict(dict, "PYTHONPATH", PYTHONPATH) ||
+ !decode_to_dict(dict, "VPATH", VPATH) ||
+ !decode_to_dict(dict, "PLATLIBDIR", PLATLIBDIR) ||
+ !decode_to_dict(dict, "PYDEBUGEXT", PYDEBUGEXT) ||
+ !int_to_dict(dict, "VERSION_MAJOR", PY_MAJOR_VERSION) ||
+ !int_to_dict(dict, "VERSION_MINOR", PY_MINOR_VERSION) ||
+ !decode_to_dict(dict, "PYWINVER", PYWINVER) ||
+ !wchar_to_dict(dict, "EXE_SUFFIX", EXE_SUFFIX) ||
+ !env_to_dict(dict, "ENV_PATH", 0) ||
+ !env_to_dict(dict, "ENV_PYTHONHOME", 0) ||
+ !env_to_dict(dict, "ENV_PYTHONEXECUTABLE", 0) ||
+ !env_to_dict(dict, "ENV___PYVENV_LAUNCHER__", 1) ||
+ !progname_to_dict(dict, "real_executable") ||
+ !library_to_dict(dict, "library") ||
+ !wchar_to_dict(dict, "executable_dir", NULL) ||
+ !wchar_to_dict(dict, "py_setpath", _PyPathConfig_GetGlobalModuleSearchPath()) ||
+ !funcs_to_dict(dict, config->pathconfig_warnings) ||
+#ifndef MS_WINDOWS
+ PyDict_SetItemString(dict, "winreg", Py_None) < 0 ||
+#endif
+ PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()) < 0
+ ) {
+ Py_DECREF(co);
+ Py_DECREF(dict);
+ _PyErr_WriteUnraisableMsg("error evaluating initial values", NULL);
+ return PyStatus_Error("error evaluating initial values");
+ }
+
+ PyObject *r = PyEval_EvalCode(co, dict, dict);
+ Py_DECREF(co);
+
+ if (!r) {
+ Py_DECREF(dict);
+ _PyErr_WriteUnraisableMsg("error evaluating path", NULL);
+ return PyStatus_Error("error evaluating path");
+ }
+ Py_DECREF(r);
+
+#if 0
+ PyObject *it = PyObject_GetIter(configDict);
+ for (PyObject *k = PyIter_Next(it); k; k = PyIter_Next(it)) {
+ if (!strcmp("__builtins__", PyUnicode_AsUTF8(k))) {
+ Py_DECREF(k);
+ continue;
+ }
+ fprintf(stderr, "%s = ", PyUnicode_AsUTF8(k));
+ PyObject *o = PyDict_GetItem(configDict, k);
+ o = PyObject_Repr(o);
+ fprintf(stderr, "%s\n", PyUnicode_AsUTF8(o));
+ Py_DECREF(o);
+ Py_DECREF(k);
+ }
+ Py_DECREF(it);
+#endif
- status = calculate_path(&calculate, pathconfig);
- if (_PyStatus_EXCEPTION(status)) {
- goto done;
+ if (_PyConfig_FromDict(config, configDict) < 0) {
+ _PyErr_WriteUnraisableMsg("reading getpath results", NULL);
+ Py_DECREF(dict);
+ return PyStatus_Error("error getting getpath results");
}
- /* program_full_path must an either an empty string or an absolute path */
- assert(wcslen(pathconfig->program_full_path) == 0
- || _Py_isabs(pathconfig->program_full_path));
-
- status = _PyStatus_OK();
+ Py_DECREF(dict);
-done:
- calculate_free(&calculate);
- return status;
-}
-
-#ifdef __cplusplus
+ return _PyStatus_OK();
}
-#endif