Skip to content

Commit

Permalink
gh-99204: Calculate base_executable by alternate names in POSIX venvs
Browse files Browse the repository at this point in the history
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 <vfazio@gmail.com>
  • Loading branch information
vfazio committed Nov 9, 2022
1 parent 4a1c58d commit e427dea
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 0 deletions.
31 changes: 31 additions & 0 deletions Lib/test/test_getpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,37 @@ def test_venv_changed_name_posix(self):
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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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>").
19 changes: 19 additions & 0 deletions Modules/getpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,25 @@ def search_up(prefix, *landmarks, test=isfile):
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
Expand Down

0 comments on commit e427dea

Please sign in to comment.