diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py index 232b6805284354..9bbe0a9447891b 100644 --- a/Lib/test/test_getpath.py +++ b/Lib/test/test_getpath.py @@ -328,6 +328,38 @@ def test_venv_posix(self): actual = getpath(ns, expected) self.assertEqual(expected, actual) + def test_venv_changed_name_posix(self): + "Test a venv layout on *nix." + ns = MockPosixNamespace( + argv0="python", + PREFIX="/usr", + ENV_PATH="/venv/bin:/usr/bin", + ) + ns.add_known_xfile("/usr/bin/python3") + ns.add_known_xfile("/venv/bin/python") + ns.add_known_link("/venv/bin/python", "/usr/bin/python3") + 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/python3", + 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/2021-12-16-15-04-58.bpo-46028.zfWacB.rst b/Misc/NEWS.d/next/Core and Builtins/2021-12-16-15-04-58.bpo-46028.zfWacB.rst new file mode 100644 index 00000000000000..cc34c0fa2405bb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-12-16-15-04-58.bpo-46028.zfWacB.rst @@ -0,0 +1,3 @@ +Fixes calculation of :data:`sys._base_executable` when inside a virtual +environment that uses symlinks with different binary names than the base +environment provides. diff --git a/Modules/getpath.py b/Modules/getpath.py index 37d2ea03b0bbd2..7dda31d9becac8 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -351,7 +351,18 @@ def search_up(prefix, *landmarks, test=isfile): key, had_equ, value = line.partition('=') if had_equ and key.strip().lower() == 'home': executable_dir = real_executable_dir = value.strip() - base_executable = joinpath(executable_dir, basename(executable)) + if not base_executable: + # First try to resolve symlinked executables, since that may be + # more accurate than assuming the executable in 'home'. + try: + base_executable = realpath(executable) + if base_executable == executable: + # No change, so probably not a link. Clear it and fall back + base_executable = '' + except OSError: + pass + if not base_executable: + base_executable = joinpath(executable_dir, basename(executable)) break else: venv_prefix = None
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: