[Python-checkins] bpo-35872 and bpo-35873: Clears __PYVENV_LAUNCHER__ variable (GH-11745)

Steve Dower webhook-mailer at python.org
Mon Feb 4 02:19:42 EST 2019


https://github.com/python/cpython/commit/a8474d025cab794257d2fd0bea67840779b9351f
commit: a8474d025cab794257d2fd0bea67840779b9351f
branch: master
author: Steve Dower <steve.dower at microsoft.com>
committer: GitHub <noreply at github.com>
date: 2019-02-03T23:19:38-08:00
summary:

bpo-35872 and bpo-35873: Clears __PYVENV_LAUNCHER__ variable (GH-11745)

After reading __PYVENV_LAUNCHER__ we now set sys._base_executable value for later use.
Make the same changes for macOS to avoid extra platform checks.

files:
A Misc/NEWS.d/next/Windows/2019-02-02-15-56-50.bpo-35873.UW-qS9.rst
A Misc/NEWS.d/next/Windows/2019-02-02-15-57-19.bpo-35872.Bba2n7.rst
M Lib/multiprocessing/popen_spawn_win32.py
M Lib/multiprocessing/spawn.py
M Lib/site.py
M Lib/test/test_venv.py
M Lib/venv/__init__.py

diff --git a/Lib/multiprocessing/popen_spawn_win32.py b/Lib/multiprocessing/popen_spawn_win32.py
index 3b92c8a2b4ae..de4c5ecf1fa0 100644
--- a/Lib/multiprocessing/popen_spawn_win32.py
+++ b/Lib/multiprocessing/popen_spawn_win32.py
@@ -19,6 +19,13 @@
 WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
 
 
+def _path_eq(p1, p2):
+    return p1 == p2 or os.path.normcase(p1) == os.path.normcase(p2)
+
+WINENV = (hasattr(sys, '_base_executable') and
+          not _path_eq(sys.executable, sys._base_executable))
+
+
 def _close_handles(*handles):
     for handle in handles:
         _winapi.CloseHandle(handle)
@@ -50,12 +57,23 @@ def __init__(self, process_obj):
                                      pipe_handle=rhandle)
         cmd = ' '.join('"%s"' % x for x in cmd)
 
+        python_exe = spawn.get_executable()
+
+        # bpo-35797: When running in a venv, we bypass the redirect
+        # executor and launch our base Python.
+        if WINENV and _path_eq(python_exe, sys.executable):
+            python_exe = sys._base_executable
+            env = os.environ.copy()
+            env["__PYVENV_LAUNCHER__"] = sys.executable
+        else:
+            env = None
+
         with open(wfd, 'wb', closefd=True) as to_child:
             # start process
             try:
                 hp, ht, pid, tid = _winapi.CreateProcess(
-                    spawn.get_executable(), cmd,
-                    None, None, False, 0, None, None, None)
+                    python_exe, cmd,
+                    env, None, False, 0, None, None, None)
                 _winapi.CloseHandle(ht)
             except:
                 _winapi.CloseHandle(rhandle)
diff --git a/Lib/multiprocessing/spawn.py b/Lib/multiprocessing/spawn.py
index 860fa4ceb5ce..6759351f13ab 100644
--- a/Lib/multiprocessing/spawn.py
+++ b/Lib/multiprocessing/spawn.py
@@ -29,19 +29,12 @@
 if sys.platform != 'win32':
     WINEXE = False
     WINSERVICE = False
-    _WINENV = False
 else:
     WINEXE = getattr(sys, 'frozen', False)
     WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
-    _WINENV = '__PYVENV_LAUNCHER__' in os.environ
 
 if WINSERVICE:
     _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
-elif _WINENV:
-    # bpo-35797: When running in a venv, we need to bypass the redirect
-    # executor and launch our base Python.
-    import _winapi
-    _python_exe = _winapi.GetModuleFileName(0)
 else:
     _python_exe = sys.executable
 
diff --git a/Lib/site.py b/Lib/site.py
index ffd132b389e1..ad1146332b0a 100644
--- a/Lib/site.py
+++ b/Lib/site.py
@@ -457,7 +457,14 @@ def venv(known_paths):
 
     env = os.environ
     if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env:
-        executable = os.environ['__PYVENV_LAUNCHER__']
+        executable = sys._base_executable = os.environ['__PYVENV_LAUNCHER__']
+    elif sys.platform == 'win32' and '__PYVENV_LAUNCHER__' in env:
+        executable = sys.executable
+        import _winapi
+        sys._base_executable = _winapi.GetModuleFileName(0)
+        # bpo-35873: Clear the environment variable to avoid it being
+        # inherited by child processes.
+        del os.environ['__PYVENV_LAUNCHER__']
     else:
         executable = sys.executable
     exe_dir, _ = os.path.split(os.path.abspath(executable))
diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py
index 6096b9df45bf..347544a67722 100644
--- a/Lib/test/test_venv.py
+++ b/Lib/test/test_venv.py
@@ -52,10 +52,7 @@ def setUp(self):
             self.bindir = 'bin'
             self.lib = ('lib', 'python%d.%d' % sys.version_info[:2])
             self.include = 'include'
-        if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in os.environ:
-            executable = os.environ['__PYVENV_LAUNCHER__']
-        else:
-            executable = sys.executable
+        executable = getattr(sys, '_base_executable', sys.executable)
         self.exe = os.path.split(executable)[-1]
 
     def tearDown(self):
@@ -100,11 +97,7 @@ def test_defaults(self):
         else:
             self.assertFalse(os.path.exists(p))
         data = self.get_text_file_contents('pyvenv.cfg')
-        if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__'
-                                         in os.environ):
-            executable =  os.environ['__PYVENV_LAUNCHER__']
-        else:
-            executable = sys.executable
+        executable = getattr(sys, '_base_executable', sys.executable)
         path = os.path.dirname(executable)
         self.assertIn('home = %s' % path, data)
         fn = self.get_env_file(self.bindir, self.exe)
diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py
index 8f9e3138474a..d5ab38958bb2 100644
--- a/Lib/venv/__init__.py
+++ b/Lib/venv/__init__.py
@@ -106,10 +106,7 @@ def create_if_needed(d):
         context.prompt = '(%s) ' % prompt
         create_if_needed(env_dir)
         env = os.environ
-        if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env:
-            executable = os.environ['__PYVENV_LAUNCHER__']
-        else:
-            executable = sys.executable
+        executable = getattr(sys, '_base_executable', sys.executable)
         dirname, exename = os.path.split(os.path.abspath(executable))
         context.executable = executable
         context.python_dir = dirname
diff --git a/Misc/NEWS.d/next/Windows/2019-02-02-15-56-50.bpo-35873.UW-qS9.rst b/Misc/NEWS.d/next/Windows/2019-02-02-15-56-50.bpo-35873.UW-qS9.rst
new file mode 100644
index 000000000000..a9ce777a4bd3
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2019-02-02-15-56-50.bpo-35873.UW-qS9.rst
@@ -0,0 +1 @@
+Prevents venv paths being inherited by child processes
diff --git a/Misc/NEWS.d/next/Windows/2019-02-02-15-57-19.bpo-35872.Bba2n7.rst b/Misc/NEWS.d/next/Windows/2019-02-02-15-57-19.bpo-35872.Bba2n7.rst
new file mode 100644
index 000000000000..be293c5b5255
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2019-02-02-15-57-19.bpo-35872.Bba2n7.rst
@@ -0,0 +1 @@
+Uses the base Python executable when invoking venv in a virtual environment



More information about the Python-checkins mailing list