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 <vfazio@gmail.com>

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
This commit is contained in:
Vincent Fazio 2022-11-10 10:26:42 -06:00 committed by GitHub
parent f1680c3fbf
commit c41b13d39c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 0 deletions

View File

@ -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(

View File

@ -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>").

View File

@ -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