[Python-checkins] bpo-45337: Use the realpath of the new executable when creating a venv on Windows (GH-28663)

miss-islington webhook-mailer at python.org
Thu Oct 7 18:55:13 EDT 2021


https://github.com/python/cpython/commit/eabca6e593269301a0b7a36c4dc3525f04f5bb36
commit: eabca6e593269301a0b7a36c4dc3525f04f5bb36
branch: 3.10
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: miss-islington <31488909+miss-islington at users.noreply.github.com>
date: 2021-10-07T15:55:05-07:00
summary:

bpo-45337: Use the realpath of the new executable when creating a venv on Windows (GH-28663)

(cherry picked from commit 6811fdaec825bd6ab64e358a4b480108f5634d2d)

Co-authored-by: Steve Dower <steve.dower at python.org>

files:
A Misc/NEWS.d/next/Windows/2021-09-30-23-17-27.bpo-45337.qg7U_h.rst
M Lib/test/test_venv.py
M Lib/venv/__init__.py

diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py
index 098ba17af5975..94d626598bac3 100644
--- a/Lib/test/test_venv.py
+++ b/Lib/test/test_venv.py
@@ -150,14 +150,20 @@ def test_prompt(self):
     def test_upgrade_dependencies(self):
         builder = venv.EnvBuilder()
         bin_path = 'Scripts' if sys.platform == 'win32' else 'bin'
-        python_exe = 'python.exe' if sys.platform == 'win32' else 'python'
+        python_exe = os.path.split(sys.executable)[1]
         with tempfile.TemporaryDirectory() as fake_env_dir:
+            expect_exe = os.path.normcase(
+                os.path.join(fake_env_dir, bin_path, python_exe)
+            )
+            if sys.platform == 'win32':
+                expect_exe = os.path.normcase(os.path.realpath(expect_exe))
 
             def pip_cmd_checker(cmd):
+                cmd[0] = os.path.normcase(cmd[0])
                 self.assertEqual(
                     cmd,
                     [
-                        os.path.join(fake_env_dir, bin_path, python_exe),
+                        expect_exe,
                         '-m',
                         'pip',
                         'install',
diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py
index 8009deb3ea700..ce1f5d710ad76 100644
--- a/Lib/venv/__init__.py
+++ b/Lib/venv/__init__.py
@@ -142,6 +142,20 @@ def create_if_needed(d):
         context.bin_name = binname
         context.env_exe = os.path.join(binpath, exename)
         create_if_needed(binpath)
+        # Assign and update the command to use when launching the newly created
+        # environment, in case it isn't simply the executable script (e.g. bpo-45337)
+        context.env_exec_cmd = context.env_exe
+        if sys.platform == 'win32':
+            # bpo-45337: Fix up env_exec_cmd to account for file system redirections.
+            # Some redirects only apply to CreateFile and not CreateProcess
+            real_env_exe = os.path.realpath(context.env_exe)
+            if os.path.normcase(real_env_exe) != os.path.normcase(context.env_exe):
+                logger.warning('Actual environment location may have moved due to '
+                               'redirects, links or junctions.\n'
+                               '  Requested location: "%s"\n'
+                               '  Actual location:    "%s"',
+                               context.env_exe, real_env_exe)
+                context.env_exec_cmd = real_env_exe
         return context
 
     def create_configuration(self, context):
@@ -293,8 +307,8 @@ def _setup_pip(self, context):
         # We run ensurepip in isolated mode to avoid side effects from
         # environment vars, the current directory and anything else
         # intended for the global Python environment
-        cmd = [context.env_exe, '-Im', 'ensurepip', '--upgrade',
-                                                    '--default-pip']
+        cmd = [context.env_exec_cmd, '-Im', 'ensurepip', '--upgrade',
+                                                         '--default-pip']
         subprocess.check_output(cmd, stderr=subprocess.STDOUT)
 
     def setup_scripts(self, context):
@@ -394,11 +408,7 @@ def upgrade_dependencies(self, context):
         logger.debug(
             f'Upgrading {CORE_VENV_DEPS} packages in {context.bin_path}'
         )
-        if sys.platform == 'win32':
-            python_exe = os.path.join(context.bin_path, 'python.exe')
-        else:
-            python_exe = os.path.join(context.bin_path, 'python')
-        cmd = [python_exe, '-m', 'pip', 'install', '--upgrade']
+        cmd = [context.env_exec_cmd, '-m', 'pip', 'install', '--upgrade']
         cmd.extend(CORE_VENV_DEPS)
         subprocess.check_call(cmd)
 
diff --git a/Misc/NEWS.d/next/Windows/2021-09-30-23-17-27.bpo-45337.qg7U_h.rst b/Misc/NEWS.d/next/Windows/2021-09-30-23-17-27.bpo-45337.qg7U_h.rst
new file mode 100644
index 0000000000000..007ee87195d6e
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2021-09-30-23-17-27.bpo-45337.qg7U_h.rst
@@ -0,0 +1,4 @@
+venv now warns when the created environment may need to be accessed at a
+different path, due to redirections, links or junctions. It also now
+correctly installs or upgrades components when the alternate path is
+required.



More information about the Python-checkins mailing list