From 846ed52f653c981e83dc57013682e808e573afc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Fri, 31 Jan 2025 03:30:54 +0000 Subject: [PATCH 1/2] GH-87915: create a venv executable with the same name as sys.executable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Lib/test/test_venv.py | 11 +++++++++++ Lib/venv/__init__.py | 13 +++++++++++-- .../2025-01-31-03-36-13.gh-issue-129382.TADOXp.rst | 2 ++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-31-03-36-13.gh-issue-129382.TADOXp.rst diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 6e23097deaf221..c37f7c0f82b911 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -888,6 +888,17 @@ def test_venvwlauncher(self): except subprocess.CalledProcessError: self.fail("venvwlauncher.exe did not run %s" % exename) + def test_ensure_sys_executable_name(self): + """ + Test that we create a executable with the same name as sys.executable. + """ + rmtree(self.env_dir) + executable_dir = os.path.dirname(sys.executable) + with patch('sys.executable', os.path.join(executable_dir, 'some-custom-name')): + venv.create(self.env_dir) + scripts_dir = os.path.join(self.env_dir, self.bindir) + self.assertIn('some-custom-name', os.listdir(scripts_dir)) + @requireVenvCreate class EnsurePipTest(BaseTest): diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index dc4c9ef3531991..61ee02da4fdcbc 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -314,9 +314,14 @@ def setup_python(self, context): if not os.path.islink(path): os.chmod(path, 0o755) - suffixes = ['python', 'python3', f'python3.{sys.version_info[1]}'] + suffixes = { + 'python', + 'python3', + f'python3.{sys.version_info[1]}', + os.path.basename(sys.executable), + } if sys.version_info[:2] == (3, 14): - suffixes.append('𝜋thon') + suffixes.add('𝜋thon') for suffix in suffixes: path = os.path.join(binpath, suffix) if not os.path.exists(path): @@ -388,6 +393,10 @@ def setup_python(self, context): f'pythonw{exe_t}{exe_d}.exe': pythonw_exe, } + for sources_dict in (link_sources, copy_sources): + if exename not in sources_dict: + sources_dict[exename] = python_exe + do_copies = True if self.symlinks: do_copies = False diff --git a/Misc/NEWS.d/next/Library/2025-01-31-03-36-13.gh-issue-129382.TADOXp.rst b/Misc/NEWS.d/next/Library/2025-01-31-03-36-13.gh-issue-129382.TADOXp.rst new file mode 100644 index 00000000000000..e48753231439a6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-31-03-36-13.gh-issue-129382.TADOXp.rst @@ -0,0 +1,2 @@ +We now make sure :mod:`venv` creates an executable with the same name as +:data:`sys.executable` in environments. From 9628e3dd8286dbfc1df587c2c07086a25c941181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Wed, 2 Apr 2025 06:11:01 +0100 Subject: [PATCH 2/2] Add missing entries on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Lib/venv/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index 61ee02da4fdcbc..f0bf57722827f0 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -344,6 +344,7 @@ def setup_python(self, context): exename = os.path.basename(context.env_exe) exe_stem = os.path.splitext(exename)[0] exe_d = '_d' if os.path.normcase(exe_stem).endswith('_d') else '' + current_exename = os.path.basename(sys.executable) if sysconfig.is_python_build(): scripts = dirname else: @@ -355,6 +356,7 @@ def setup_python(self, context): link_sources = { 'python.exe': python_exe, f'python{exe_d}.exe': python_exe, + current_exename: python_exe, 'pythonw.exe': pythonw_exe, f'pythonw{exe_d}.exe': pythonw_exe, } @@ -363,6 +365,7 @@ def setup_python(self, context): copy_sources = { 'python.exe': python_exe, f'python{exe_d}.exe': python_exe, + current_exename: python_exe, 'pythonw.exe': pythonw_exe, f'pythonw{exe_d}.exe': pythonw_exe, } @@ -375,6 +378,7 @@ def setup_python(self, context): f'python{exe_d}.exe': python_exe, f'python{exe_t}.exe': python_exe, f'python{exe_t}{exe_d}.exe': python_exe, + current_exename: python_exe, 'pythonw.exe': pythonw_exe, f'pythonw{exe_d}.exe': pythonw_exe, f'pythonw{exe_t}.exe': pythonw_exe, @@ -387,6 +391,7 @@ def setup_python(self, context): f'python{exe_d}.exe': python_exe, f'python{exe_t}.exe': python_exe, f'python{exe_t}{exe_d}.exe': python_exe, + current_exename: python_exe, 'pythonw.exe': pythonw_exe, f'pythonw{exe_d}.exe': pythonw_exe, f'pythonw{exe_t}.exe': pythonw_exe,