Skip to content

Commit c9f2177

Browse files
gh-99204: Calculate base_executable by alternate names in POSIX venvs (GH-99206)
Check to see if `base_executable` exists. If it does not, attempt to use known alternative names of the python binary to find an executable in the path specified by `home`. If no alternative is found, previous behavior is preserved. Signed-off-by: Vincent Fazio <[email protected]> (cherry picked from commit c41b13d) Co-authored-by: Vincent Fazio <[email protected]> Signed-off-by: Vincent Fazio <[email protected]>
1 parent f9a68be commit c9f2177

File tree

3 files changed

+54
-0
lines changed

3 files changed

+54
-0
lines changed

Lib/test/test_getpath.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,37 @@ def test_venv_changed_name_posix(self):
383383
actual = getpath(ns, expected)
384384
self.assertEqual(expected, actual)
385385

386+
def test_venv_changed_name_copy_posix(self):
387+
"Test a venv --copies layout on *nix that lacks a distributed 'python'"
388+
ns = MockPosixNamespace(
389+
argv0="python",
390+
PREFIX="/usr",
391+
ENV_PATH="/venv/bin:/usr/bin",
392+
)
393+
ns.add_known_xfile("/usr/bin/python9")
394+
ns.add_known_xfile("/venv/bin/python")
395+
ns.add_known_file("/usr/lib/python9.8/os.py")
396+
ns.add_known_dir("/usr/lib/python9.8/lib-dynload")
397+
ns.add_known_file("/venv/pyvenv.cfg", [
398+
r"home = /usr/bin"
399+
])
400+
expected = dict(
401+
executable="/venv/bin/python",
402+
prefix="/usr",
403+
exec_prefix="/usr",
404+
base_executable="/usr/bin/python9",
405+
base_prefix="/usr",
406+
base_exec_prefix="/usr",
407+
module_search_paths_set=1,
408+
module_search_paths=[
409+
"/usr/lib/python98.zip",
410+
"/usr/lib/python9.8",
411+
"/usr/lib/python9.8/lib-dynload",
412+
],
413+
)
414+
actual = getpath(ns, expected)
415+
self.assertEqual(expected, actual)
416+
386417
def test_symlink_normal_posix(self):
387418
"Test a 'standard' install layout via symlink on *nix"
388419
ns = MockPosixNamespace(
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix calculation of :data:`sys._base_executable` when inside a POSIX virtual
2+
environment using copies of the python binary when the base installation does
3+
not provide the executable name used by the venv. Calculation will fall back to
4+
alternative names ("python<MAJOR>", "python<MAJOR>.<MINOR>").

Modules/getpath.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,25 @@ def search_up(prefix, *landmarks, test=isfile):
375375
pass
376376
if not base_executable:
377377
base_executable = joinpath(executable_dir, basename(executable))
378+
# It's possible "python" is executed from within a posix venv but that
379+
# "python" is not available in the "home" directory as the standard
380+
# `make install` does not create it and distros often do not provide it.
381+
#
382+
# In this case, try to fall back to known alternatives
383+
if os_name != 'nt' and not isfile(base_executable):
384+
base_exe = basename(executable)
385+
for candidate in (DEFAULT_PROGRAM_NAME, f'python{VERSION_MAJOR}.{VERSION_MINOR}'):
386+
candidate += EXE_SUFFIX if EXE_SUFFIX else ''
387+
if base_exe == candidate:
388+
continue
389+
candidate = joinpath(executable_dir, candidate)
390+
# Only set base_executable if the candidate exists.
391+
# If no candidate succeeds, subsequent errors related to
392+
# base_executable (like FileNotFoundError) remain in the
393+
# context of the original executable name
394+
if isfile(candidate):
395+
base_executable = candidate
396+
break
378397
break
379398
else:
380399
venv_prefix = None

0 commit comments

Comments
 (0)