summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_getpath.py31
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-11-07-08-17-12.gh-issue-99204.Mf4hMD.rst4
-rw-r--r--Modules/getpath.py19
3 files changed, 54 insertions, 0 deletions
diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py
index 12d5244..7a22a9a 100644
--- a/Lib/test/test_getpath.py
+++ b/Lib/test/test_getpath.py
@@ -382,6 +382,37 @@ class MockGetPathTests(unittest.TestCase):
actual = getpath(ns, expected)
self.assertEqual(expected, actual)
+ def test_venv_changed_name_copy_posix(self):
+ "Test a venv --copies layout on *nix that lacks a distributed 'python'"
+ ns = MockPosixNamespace(
+ argv0="python",
+ PREFIX="/usr",
+ ENV_PATH="/venv/bin:/usr/bin",
+ )
+ ns.add_known_xfile("/usr/bin/python9")
+ ns.add_known_xfile("/venv/bin/python")
+ ns.add_known_file("/usr/lib/python9.8/os.py")
+ ns.add_known_dir("/usr/lib/python9.8/lib-dynload")
+ ns.add_known_file("/venv/pyvenv.cfg", [
+ r"home = /usr/bin"
+ ])
+ expected = dict(
+ executable="/venv/bin/python",
+ prefix="/usr",
+ exec_prefix="/usr",
+ base_executable="/usr/bin/python9",
+ base_prefix="/usr",
+ base_exec_prefix="/usr",
+ module_search_paths_set=1,
+ module_search_paths=[
+ "/usr/lib/python98.zip",
+ "/usr/lib/python9.8",
+ "/usr/lib/python9.8/lib-dynload",
+ ],
+ )
+ actual = getpath(ns, expected)
+ self.assertEqual(expected, actual)
+
def test_symlink_normal_posix(self):
"Test a 'standard' install layout via symlink on *nix"
ns = MockPosixNamespace(
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-11-07-08-17-12.gh-issue-99204.Mf4hMD.rst b/Misc/NEWS.d/next/Core and Builtins/2022-11-07-08-17-12.gh-issue-99204.Mf4hMD.rst
new file mode 100644
index 0000000..571cdd0
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-11-07-08-17-12.gh-issue-99204.Mf4hMD.rst
@@ -0,0 +1,4 @@
+Fix calculation of :data:`sys._base_executable` when inside a POSIX virtual
+environment using copies of the python binary when the base installation does
+not provide the executable name used by the venv. Calculation will fall back to
+alternative names ("python<MAJOR>", "python<MAJOR>.<MINOR>").
diff --git a/Modules/getpath.py b/Modules/getpath.py
index 90a6473..d24b152 100644
--- a/Modules/getpath.py
+++ b/Modules/getpath.py
@@ -375,6 +375,25 @@ if not home and not py_setpath:
pass
if not base_executable:
base_executable = joinpath(executable_dir, basename(executable))
+ # It's possible "python" is executed from within a posix venv but that
+ # "python" is not available in the "home" directory as the standard
+ # `make install` does not create it and distros often do not provide it.
+ #
+ # In this case, try to fall back to known alternatives
+ if os_name != 'nt' and not isfile(base_executable):
+ base_exe = basename(executable)
+ for candidate in (DEFAULT_PROGRAM_NAME, f'python{VERSION_MAJOR}.{VERSION_MINOR}'):
+ candidate += EXE_SUFFIX if EXE_SUFFIX else ''
+ if base_exe == candidate:
+ continue
+ candidate = joinpath(executable_dir, candidate)
+ # Only set base_executable if the candidate exists.
+ # If no candidate succeeds, subsequent errors related to
+ # base_executable (like FileNotFoundError) remain in the
+ # context of the original executable name
+ if isfile(candidate):
+ base_executable = candidate
+ break
break
else:
venv_prefix = None