From d12ab930f87cf86bd09c8110a6c1a3ee4316e8ee Mon Sep 17 00:00:00 2001 From: Vyron Vasileiadis Date: Sun, 19 Jul 2026 18:21:11 +0300 Subject: [PATCH] gh-154160: Make sys.executable absolute when resolved from a relative PATH entry When the interpreter is started by a bare program name and found through a relative directory in PATH, getpath left sys.executable as that relative path, contrary to its documented behaviour of being an absolute path. This is a regression from the getpath.c rewrite in 3.11. Absolutize the executable resolved from PATH, matching the sibling branch that already calls abspath() when the program name itself contains a separator. --- Lib/test/test_getpath.py | 26 +++++++++++++++++++ ...-07-19-18-30-00.gh-issue-154160.XnXeqS.rst | 3 +++ Modules/getpath.py | 4 ++- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-07-19-18-30-00.gh-issue-154160.XnXeqS.rst diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py index 83f09f3495547ac..c545858e639835f 100644 --- a/Lib/test/test_getpath.py +++ b/Lib/test/test_getpath.py @@ -290,6 +290,32 @@ def test_normal_posix(self): actual = getpath(ns, expected) self.assertEqual(expected, actual) + def test_relative_path_env_posix(self): + "Resolving the executable from a relative PATH entry yields an absolute path (gh-154160)" + ns = MockPosixNamespace( + PREFIX="/usr", + argv0="python", + ENV_PATH="usr/bin", + ) + ns.add_known_xfile("usr/bin/python") + ns.add_known_xfile("/Absolute/usr/bin/python") + ns.add_known_file("/usr/lib/python9.8/os.py") + ns.add_known_dir("/usr/lib/python9.8/lib-dynload") + expected = dict( + executable="/Absolute/usr/bin/python", + base_executable="/Absolute/usr/bin/python", + prefix="/usr", + 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_buildpath_posix(self): """Test an in-build-tree layout on POSIX. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-19-18-30-00.gh-issue-154160.XnXeqS.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-19-18-30-00.gh-issue-154160.XnXeqS.rst new file mode 100644 index 000000000000000..d51bea4221f942a --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-19-18-30-00.gh-issue-154160.XnXeqS.rst @@ -0,0 +1,3 @@ +Fixed :data:`sys.executable` being a relative path when the interpreter was +found through a relative entry in :envvar:`PATH`. It is now made absolute, as +documented. diff --git a/Modules/getpath.py b/Modules/getpath.py index 6199567bd777aa0..32a64feef1ee6ca 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -285,7 +285,9 @@ def search_up(prefix, *landmarks, test=isfile): for p in ENV_PATH.split(DELIM): p = joinpath(p, program_name) if isxfile(p): - executable = p + # A relative PATH entry would otherwise leave executable + # relative, contrary to the documented behaviour (gh-154160). + executable = abspath(p) break if not executable: