Skip to content

Commit f9ef949

Browse files
committed
address review comments
1 parent a4f000b commit f9ef949

File tree

2 files changed

+38
-35
lines changed

2 files changed

+38
-35
lines changed

Lib/test/test_venv.py

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -900,20 +900,17 @@ def test_executable_symlink(self):
900900
Test creation using a symlink to python executable.
901901
"""
902902
rmtree(self.env_dir)
903-
with tempfile.TemporaryDirectory() as symlink_dir:
904-
executable_symlink = os.path.join(
905-
os.path.realpath(symlink_dir),
906-
os.path.basename(sys.executable))
907-
os.symlink(os.path.abspath(sys.executable), executable_symlink)
908-
cmd = [executable_symlink, "-m", "venv", "--without-pip",
909-
self.env_dir]
903+
exe = pathlib.Path(sys.executable).absolute()
904+
with tempfile.TemporaryDirectory() as tmp_dir:
905+
symlink_dir = pathlib.Path(tmp_dir).resolve(strict=True)
906+
exe_symlink = symlink_dir / exe.name
907+
exe_symlink.symlink_to(exe)
908+
cmd = [exe_symlink, "-m", "venv", "--without-pip", self.env_dir]
910909
subprocess.check_call(cmd)
911910
data = self.get_text_file_contents('pyvenv.cfg')
912-
executable = sys._base_executable
913-
path = os.path.dirname(executable)
911+
path = os.path.dirname(sys._base_executable)
914912
self.assertIn('home = %s' % path, data)
915-
self.assertIn('executable = %s' %
916-
os.path.realpath(sys.executable), data)
913+
self.assertIn('executable = %s' % exe.resolve(), data)
917914

918915
@requires_subprocess()
919916
@unittest.skipIf(os.name == 'nt', 'not relevant on Windows')
@@ -924,24 +921,18 @@ def test_tree_symlink(self):
924921
Test creation using a symlink to python tree.
925922
"""
926923
rmtree(self.env_dir)
927-
executable_abspath = os.path.abspath(sys._base_executable)
928-
tree_abspath = os.path.dirname(os.path.dirname(executable_abspath))
929-
with tempfile.TemporaryDirectory() as symlink_dir:
930-
tree_symlink = os.path.join(
931-
os.path.realpath(symlink_dir),
932-
os.path.basename(tree_abspath))
933-
executable_symlink = os.path.join(
934-
tree_symlink,
935-
os.path.basename(os.path.dirname(executable_abspath)),
936-
os.path.basename(sys._base_executable))
937-
os.symlink(tree_abspath, tree_symlink)
938-
cmd = [executable_symlink, "-m", "venv", "--without-pip",
939-
self.env_dir]
924+
exe = pathlib.Path(sys._base_executable).absolute()
925+
tree = exe.parent.parent
926+
with tempfile.TemporaryDirectory() as tmp_dir:
927+
symlink_dir = pathlib.Path(tmp_dir).resolve(strict=True)
928+
tree_symlink = symlink_dir / tree.name
929+
exe_symlink = tree_symlink / exe.relative_to(tree)
930+
tree_symlink.symlink_to(tree)
931+
cmd = [exe_symlink, "-m", "venv", "--without-pip", self.env_dir]
940932
subprocess.check_call(cmd)
941933
data = self.get_text_file_contents('pyvenv.cfg')
942934
self.assertIn('home = %s' % tree_symlink, data)
943-
self.assertIn('executable = %s' %
944-
os.path.realpath(sys._base_executable), data)
935+
self.assertIn('executable = %s' % exe.resolve(), data)
945936

946937

947938
@requireVenvCreate

Lib/venv/__init__.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,26 @@ def _same_path(cls, path1, path2):
133133
else:
134134
return path1 == path2
135135

136+
@classmethod
137+
def _abspath_resolve_leaf(cls, path):
138+
"""Returns the absolute path, resolving links to the last component.
139+
140+
If there's a cycle, os.path.abspath(path) is returned
141+
"""
142+
path = os.path.abspath(path)
143+
result = path
144+
while os.path.islink(result):
145+
link = os.readlink(result)
146+
if os.path.isabs(link):
147+
result = link
148+
else:
149+
result = os.path.join(os.path.dirname(result), link)
150+
result = os.path.abspath(result)
151+
if result == path:
152+
# circular links
153+
break
154+
return result
155+
136156
def ensure_directories(self, env_dir):
137157
"""
138158
Create the directories for the environment.
@@ -164,15 +184,7 @@ def create_if_needed(d):
164184
'check that your PATH environment variable is '
165185
'correctly set.')
166186
# only resolve executable symlinks, not the full chain, see gh-106045
167-
# we don't want to overwrite the executable used in context
168-
executable_ = os.path.abspath(executable)
169-
while os.path.islink(executable_):
170-
link = os.readlink(executable_)
171-
if os.path.isabs(link):
172-
executable_ = link
173-
else:
174-
executable_ = os.path.join(os.path.dirname(executable_), link)
175-
dirname, exename = os.path.split(executable_)
187+
dirname, exename = os.path.split(self._abspath_resolve_leaf(executable))
176188
if sys.platform == 'win32':
177189
# Always create the simplest name in the venv. It will either be a
178190
# link back to executable, or a copy of the appropriate launcher

0 commit comments

Comments
 (0)