Bug report
Bug description:
The current getpath.py code tries determining base_prefix/base_exec_prefix by searching the location of the libpython library loaded in the current process, falling back to the location of the Python interpreter executable.
| # First try to detect prefix by looking alongside our runtime library, if known |
| iflibraryandnotprefix: |
| library_dir=dirname(library) |
| ifZIP_LANDMARK: |
| ifos_name=='nt': |
| # QUIRK: Windows does not search up for ZIP file |
| ifisfile(joinpath(library_dir, ZIP_LANDMARK)): |
| prefix=library_dir |
| else: |
| prefix=search_up(library_dir, ZIP_LANDMARK) |
| ifSTDLIB_SUBDIRandSTDLIB_LANDMARKSandnotprefix: |
| ifany(isfile(joinpath(library_dir, f)) forfinSTDLIB_LANDMARKS): |
| prefix=library_dir |
| ifnotstdlib_dir_was_set_in_config: |
| stdlib_dir=joinpath(prefix, STDLIB_SUBDIR) |
| |
| |
| # Detect prefix by looking for zip file |
| ifZIP_LANDMARKandexecutable_dirandnotprefix: |
| ifos_name=='nt': |
| # QUIRK: Windows does not search up for ZIP file |
| ifisfile(joinpath(executable_dir, ZIP_LANDMARK)): |
| prefix=executable_dir |
| else: |
| prefix=search_up(executable_dir, ZIP_LANDMARK) |
| ifprefixandnotstdlib_dir_was_set_in_config: |
| stdlib_dir=joinpath(prefix, STDLIB_SUBDIR) |
| ifnotisdir(stdlib_dir): |
| stdlib_dir=None |
| |
| |
| # Detect prefix by searching from our executable location for the stdlib_dir |
| ifSTDLIB_SUBDIRandSTDLIB_LANDMARKSandexecutable_dirandnotprefix: |
| prefix=search_up(executable_dir, *STDLIB_LANDMARKS) |
| ifprefixandnotstdlib_dir: |
| stdlib_dir=joinpath(prefix, STDLIB_SUBDIR) |
Looking at the location of the libpython library in use first makes sense, as that is more reliable than looking at interpreter location — it works when embedding, where there isn't any interpreter executable, it works when the executable is not on base_prefix, etc. However, this is only currently supported on Windows and macOS framework builds.
| /* Add the runtime library's path to the dict */ |
| staticint |
| library_to_dict(PyObject*dict, constchar*key) |
| { |
| #ifdefMS_WINDOWS |
| #ifdefPy_ENABLE_SHARED |
| externHMODULEPyWin_DLLhModule; |
| if (PyWin_DLLhModule) { |
| returnwinmodule_to_dict(dict, key, PyWin_DLLhModule); |
| } |
| #endif |
| #elif defined(WITH_NEXT_FRAMEWORK) |
| staticcharmodPath[MAXPATHLEN+1]; |
| staticintmodPathInitialized=-1; |
| if (modPathInitialized<0) { |
| modPathInitialized=0; |
| |
| /* 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... */ |
| Dl_infopythonInfo; |
| if (dladdr(&Py_Initialize, &pythonInfo)) { |
| if (pythonInfo.dli_fname) { |
| strncpy(modPath, pythonInfo.dli_fname, MAXPATHLEN); |
| modPathInitialized=1; |
| } |
| } |
| } |
| if (modPathInitialized>0) { |
| returndecode_to_dict(dict, key, modPath); |
| } |
| #endif |
| returnPyDict_SetItemString(dict, key, Py_None) ==0; |
| } |
The spotty platform support stroke me as odd, especially on macOS, as I see no apparent reason for only supporting framework builds, so I looked traced back the origin of this code.
The macOS logic goes back to Python 2.0, having been introduced in 54ecc3d. At this time, we were determining base_prefix/base_exec_prefix based on the Python interpreter location, which was problematic on OS X Frameworks, as the Python interpreter is provided via a launcher. The comment traces back to 55070f5 and is unrelated to the change made by that commit, it just highlights the special case for macOS framework builds.
In GH-29041, which introduced getpath.py, rewriting the old path initialization C code in Python, the logic changed to purposefully search the libpython location before the executable location, also adding Windows support. I imagine the existing macOS code was kept as-is as a mistake, leaving it behind the WITH_NEXT_FRAMEWORK flag, maybe under the assumption it was needed for some reason.
Considering the clear intent in the code, I am treating this a bug.
cc @zooba
CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Linked PRs
Bug report
Bug description:
The current
getpath.pycode tries determiningbase_prefix/base_exec_prefixby searching the location of thelibpythonlibrary loaded in the current process, falling back to the location of the Python interpreter executable.cpython/Modules/getpath.py
Lines 559 to 594 in 7900a85
Looking at the location of the
libpythonlibrary in use first makes sense, as that is more reliable than looking at interpreter location — it works when embedding, where there isn't any interpreter executable, it works when the executable is not onbase_prefix, etc. However, this is only currently supported on Windows and macOS framework builds.cpython/Modules/getpath.c
Lines 802 to 837 in 7b8bd3b
The spotty platform support stroke me as odd, especially on macOS, as I see no apparent reason for only supporting framework builds, so I looked traced back the origin of this code.
The macOS logic goes back to Python 2.0, having been introduced in 54ecc3d. At this time, we were determining
base_prefix/base_exec_prefixbased on the Python interpreter location, which was problematic on OS X Frameworks, as the Python interpreter is provided via a launcher. The comment traces back to 55070f5 and is unrelated to the change made by that commit, it just highlights the special case for macOS framework builds.In GH-29041, which introduced
getpath.py, rewriting the old path initialization C code in Python, the logic changed to purposefully search thelibpythonlocation before the executable location, also adding Windows support. I imagine the existing macOS code was kept as-is as a mistake, leaving it behind theWITH_NEXT_FRAMEWORKflag, maybe under the assumption it was needed for some reason.Considering the clear intent in the code, I am treating this a bug.
cc @zooba
CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Linked PRs