From 340d9c29571885ce072eff99ee2a952e6364e4d2 Mon Sep 17 00:00:00 2001 From: mayeut Date: Sat, 10 Feb 2024 11:53:04 +0100 Subject: [PATCH] gh-106045: Fix ``venv`` creation from a python executable symlink --- Lib/test/test_venv.py | 51 +++++++++++++++++++ Lib/venv/__init__.py | 11 +++- Misc/ACKS | 1 + ...-02-10-11-41-42.gh-issue-106045.eVZFt2.rst | 2 + 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-02-10-11-41-42.gh-issue-106045.eVZFt2.rst diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 1ef08da326c18c..0d4d1ae2248e8d 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -791,6 +791,57 @@ def test_venv_same_path(self): else: self.assertFalse(same_path(path1, path2)) + @requires_subprocess() + @unittest.skipIf(os.name == 'nt', 'not relevant on Windows') + @unittest.skipUnless(can_symlink(), 'Needs symlinks') + def test_executable_symlink(self): + """ + Test creation using a symlink to python executable. + """ + rmtree(self.env_dir) + with tempfile.TemporaryDirectory() as symlink_dir: + executable_symlink = os.path.join( + os.path.realpath(symlink_dir), + os.path.basename(sys.executable)) + os.symlink(os.path.abspath(sys.executable), executable_symlink) + cmd = [executable_symlink, "-m", "venv", "--without-pip", + self.env_dir] + subprocess.check_call(cmd) + data = self.get_text_file_contents('pyvenv.cfg') + executable = sys._base_executable + path = os.path.dirname(executable) + self.assertIn('home = %s' % path, data) + self.assertIn('executable = %s' % + os.path.realpath(sys.executable), data) + + @requires_subprocess() + @unittest.skipIf(os.name == 'nt', 'not relevant on Windows') + @unittest.skipUnless(can_symlink(), 'Needs symlinks') + @requireVenvCreate + def test_tree_symlink(self): + """ + Test creation using a symlink to python tree. + """ + rmtree(self.env_dir) + executable_abspath = os.path.abspath(sys._base_executable) + tree_abspath = os.path.dirname(os.path.dirname(executable_abspath)) + with tempfile.TemporaryDirectory() as symlink_dir: + tree_symlink = os.path.join( + os.path.realpath(symlink_dir), + os.path.basename(tree_abspath)) + executable_symlink = os.path.join( + tree_symlink, + os.path.basename(os.path.dirname(executable_abspath)), + os.path.basename(sys._base_executable)) + os.symlink(tree_abspath, tree_symlink) + cmd = [executable_symlink, "-m", "venv", "--without-pip", + self.env_dir] + subprocess.check_call(cmd) + data = self.get_text_file_contents('pyvenv.cfg') + self.assertIn('home = %s' % tree_symlink, data) + self.assertIn('executable = %s' % + os.path.realpath(sys._base_executable), data) + @requireVenvCreate class EnsurePipTest(BaseTest): """Test venv module installation of pip.""" diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index 028e9483196694..389464ad80e143 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -164,7 +164,16 @@ def create_if_needed(d): 'Python interpreter. Provide an explicit path or ' 'check that your PATH environment variable is ' 'correctly set.') - dirname, exename = os.path.split(os.path.abspath(executable)) + # only resolve executable symlinks, not the full chain, see gh-106045 + # we don't want to overwrite the executable used in context + executable_ = os.path.abspath(executable) + while os.path.islink(executable_): + link = os.readlink(executable_) + if os.path.isabs(link): + executable_ = link + else: + executable_ = os.path.join(os.path.dirname(executable_), link) + dirname, exename = os.path.split(executable_) if sys.platform == 'win32': # Always create the simplest name in the venv. It will either be a # link back to executable, or a copy of the appropriate launcher diff --git a/Misc/ACKS b/Misc/ACKS index d94cbacf888468..afc31d936c3d5b 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -417,6 +417,7 @@ Eric Daniel Scott David Daniels Derzsi Dániel Lawrence D'Anna +Matthieu Darbois Ben Darnell Kushal Das Jonathan Dasteel diff --git a/Misc/NEWS.d/next/Library/2024-02-10-11-41-42.gh-issue-106045.eVZFt2.rst b/Misc/NEWS.d/next/Library/2024-02-10-11-41-42.gh-issue-106045.eVZFt2.rst new file mode 100644 index 00000000000000..1a3292976bc9d3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-02-10-11-41-42.gh-issue-106045.eVZFt2.rst @@ -0,0 +1,2 @@ +Fix ``venv`` creation from a python executable symlink. Patch by Matthieu +Darbois.