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..f0bf57722827f0 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): @@ -339,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: @@ -350,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, } @@ -358,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, } @@ -370,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, @@ -382,12 +391,17 @@ 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, 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.