diff --git a/Lib/multiprocessing/popen_spawn_win32.py b/Lib/multiprocessing/popen_spawn_win32.py index 3b92c8a2b4a..de4c5ecf1fa 100644 --- a/Lib/multiprocessing/popen_spawn_win32.py +++ b/Lib/multiprocessing/popen_spawn_win32.py @@ -19,6 +19,13 @@ WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False)) 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 @@ class Popen(object): 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 860fa4ceb5c..6759351f13a 100644 --- a/Lib/multiprocessing/spawn.py +++ b/Lib/multiprocessing/spawn.py @@ -29,19 +29,12 @@ __all__ = ['_main', 'freeze_support', 'set_executable', 'get_executable', 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 ffd132b389e..ad1146332b0 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 6096b9df45b..347544a6772 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -52,10 +52,7 @@ class BaseTest(unittest.TestCase): 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 @@ class BasicTest(BaseTest): 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 8f9e3138474..d5ab38958bb 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -106,10 +106,7 @@ class EnvBuilder: 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 00000000000..a9ce777a4bd --- /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 00000000000..be293c5b525 --- /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