Skip to content

Commit 6811fda

Browse files
authored
bpo-45337: Use the realpath of the new executable when creating a venv on Windows (GH-28663)
1 parent 8deb7af commit 6811fda

File tree

3 files changed

+29
-9
lines changed

3 files changed

+29
-9
lines changed

Lib/test/test_venv.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,20 @@ def test_prompt(self):
150150
def test_upgrade_dependencies(self):
151151
builder = venv.EnvBuilder()
152152
bin_path = 'Scripts' if sys.platform == 'win32' else 'bin'
153-
python_exe = 'python.exe' if sys.platform == 'win32' else 'python'
153+
python_exe = os.path.split(sys.executable)[1]
154154
with tempfile.TemporaryDirectory() as fake_env_dir:
155+
expect_exe = os.path.normcase(
156+
os.path.join(fake_env_dir, bin_path, python_exe)
157+
)
158+
if sys.platform == 'win32':
159+
expect_exe = os.path.normcase(os.path.realpath(expect_exe))
155160

156161
def pip_cmd_checker(cmd):
162+
cmd[0] = os.path.normcase(cmd[0])
157163
self.assertEqual(
158164
cmd,
159165
[
160-
os.path.join(fake_env_dir, bin_path, python_exe),
166+
expect_exe,
161167
'-m',
162168
'pip',
163169
'install',

Lib/venv/__init__.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,20 @@ def create_if_needed(d):
142142
context.bin_name = binname
143143
context.env_exe = os.path.join(binpath, exename)
144144
create_if_needed(binpath)
145+
# Assign and update the command to use when launching the newly created
146+
# environment, in case it isn't simply the executable script (e.g. bpo-45337)
147+
context.env_exec_cmd = context.env_exe
148+
if sys.platform == 'win32':
149+
# bpo-45337: Fix up env_exec_cmd to account for file system redirections.
150+
# Some redirects only apply to CreateFile and not CreateProcess
151+
real_env_exe = os.path.realpath(context.env_exe)
152+
if os.path.normcase(real_env_exe) != os.path.normcase(context.env_exe):
153+
logger.warning('Actual environment location may have moved due to '
154+
'redirects, links or junctions.\n'
155+
' Requested location: "%s"\n'
156+
' Actual location: "%s"',
157+
context.env_exe, real_env_exe)
158+
context.env_exec_cmd = real_env_exe
145159
return context
146160

147161
def create_configuration(self, context):
@@ -294,8 +308,8 @@ def _setup_pip(self, context):
294308
# We run ensurepip in isolated mode to avoid side effects from
295309
# environment vars, the current directory and anything else
296310
# intended for the global Python environment
297-
cmd = [context.env_exe, '-Im', 'ensurepip', '--upgrade',
298-
'--default-pip']
311+
cmd = [context.env_exec_cmd, '-Im', 'ensurepip', '--upgrade',
312+
'--default-pip']
299313
subprocess.check_output(cmd, stderr=subprocess.STDOUT)
300314

301315
def setup_scripts(self, context):
@@ -395,11 +409,7 @@ def upgrade_dependencies(self, context):
395409
logger.debug(
396410
f'Upgrading {CORE_VENV_DEPS} packages in {context.bin_path}'
397411
)
398-
if sys.platform == 'win32':
399-
python_exe = os.path.join(context.bin_path, 'python.exe')
400-
else:
401-
python_exe = os.path.join(context.bin_path, 'python')
402-
cmd = [python_exe, '-m', 'pip', 'install', '--upgrade']
412+
cmd = [context.env_exec_cmd, '-m', 'pip', 'install', '--upgrade']
403413
cmd.extend(CORE_VENV_DEPS)
404414
subprocess.check_call(cmd)
405415

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
venv now warns when the created environment may need to be accessed at a
2+
different path, due to redirections, links or junctions. It also now
3+
correctly installs or upgrades components when the alternate path is
4+
required.

0 commit comments

Comments
 (0)