Skip to content

Commit 44467e8

Browse files
authored
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.
1 parent 69af439 commit 44467e8

File tree

7 files changed

+39
-23
lines changed

7 files changed

+39
-23
lines changed

Lib/multiprocessing/popen_spawn_win32.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@
1818
WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
1919
WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
2020

21+
22+
def _path_eq(p1, p2):
23+
return p1 == p2 or os.path.normcase(p1) == os.path.normcase(p2)
24+
25+
WINENV = (hasattr(sys, '_base_executable') and
26+
not _path_eq(sys.executable, sys._base_executable))
27+
28+
29+
def _close_handles(*handles):
30+
for handle in handles:
31+
_winapi.CloseHandle(handle)
32+
33+
2134
#
2235
# We define a Popen class similar to the one from subprocess, but
2336
# whose constructor takes a process object as its argument.
@@ -40,12 +53,23 @@ def __init__(self, process_obj):
4053
pipe_handle=rhandle)
4154
cmd = ' '.join('"%s"' % x for x in cmd)
4255

56+
python_exe = spawn.get_executable()
57+
58+
# bpo-35797: When running in a venv, we bypass the redirect
59+
# executor and launch our base Python.
60+
if WINENV and _path_eq(python_exe, sys.executable):
61+
python_exe = sys._base_executable
62+
env = os.environ.copy()
63+
env["__PYVENV_LAUNCHER__"] = sys.executable
64+
else:
65+
env = None
66+
4367
with open(wfd, 'wb', closefd=True) as to_child:
4468
# start process
4569
try:
4670
hp, ht, pid, tid = _winapi.CreateProcess(
47-
spawn.get_executable(), cmd,
48-
None, None, False, 0, None, None, None)
71+
python_exe, cmd,
72+
env, None, False, 0, None, None, None)
4973
_winapi.CloseHandle(ht)
5074
except:
5175
_winapi.CloseHandle(rhandle)

Lib/multiprocessing/spawn.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,12 @@
2929
if sys.platform != 'win32':
3030
WINEXE = False
3131
WINSERVICE = False
32-
_WINENV = False
3332
else:
3433
WINEXE = getattr(sys, 'frozen', False)
3534
WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
36-
_WINENV = '__PYVENV_LAUNCHER__' in os.environ
3735

3836
if WINSERVICE:
3937
_python_exe = os.path.join(sys.exec_prefix, 'python.exe')
40-
elif _WINENV:
41-
# bpo-35797: When running in a venv, we need to bypass the redirect
42-
# executor and launch our base Python.
43-
import _winapi
44-
_python_exe = _winapi.GetModuleFileName(0)
4538
else:
4639
_python_exe = sys.executable
4740

Lib/site.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,14 @@ def venv(known_paths):
457457

458458
env = os.environ
459459
if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env:
460-
executable = os.environ['__PYVENV_LAUNCHER__']
460+
executable = sys._base_executable = os.environ['__PYVENV_LAUNCHER__']
461+
elif sys.platform == 'win32' and '__PYVENV_LAUNCHER__' in env:
462+
executable = sys.executable
463+
import _winapi
464+
sys._base_executable = _winapi.GetModuleFileName(0)
465+
# bpo-35873: Clear the environment variable to avoid it being
466+
# inherited by child processes.
467+
del os.environ['__PYVENV_LAUNCHER__']
461468
else:
462469
executable = sys.executable
463470
exe_dir, _ = os.path.split(os.path.abspath(executable))

Lib/test/test_venv.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,7 @@ def setUp(self):
5252
self.bindir = 'bin'
5353
self.lib = ('lib', 'python%d.%d' % sys.version_info[:2])
5454
self.include = 'include'
55-
if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in os.environ:
56-
executable = os.environ['__PYVENV_LAUNCHER__']
57-
else:
58-
executable = sys.executable
55+
executable = getattr(sys, '_base_executable', sys.executable)
5956
self.exe = os.path.split(executable)[-1]
6057

6158
def tearDown(self):
@@ -100,11 +97,7 @@ def test_defaults(self):
10097
else:
10198
self.assertFalse(os.path.exists(p))
10299
data = self.get_text_file_contents('pyvenv.cfg')
103-
if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__'
104-
in os.environ):
105-
executable = os.environ['__PYVENV_LAUNCHER__']
106-
else:
107-
executable = sys.executable
100+
executable = getattr(sys, '_base_executable', sys.executable)
108101
path = os.path.dirname(executable)
109102
self.assertIn('home = %s' % path, data)
110103
fn = self.get_env_file(self.bindir, self.exe)

Lib/venv/__init__.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,7 @@ def create_if_needed(d):
106106
context.prompt = '(%s) ' % prompt
107107
create_if_needed(env_dir)
108108
env = os.environ
109-
if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env:
110-
executable = os.environ['__PYVENV_LAUNCHER__']
111-
else:
112-
executable = sys.executable
109+
executable = getattr(sys, '_base_executable', sys.executable)
113110
dirname, exename = os.path.split(os.path.abspath(executable))
114111
context.executable = executable
115112
context.python_dir = dirname
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Prevents venv paths being inherited by child processes
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Uses the base Python executable when invoking venv in a virtual environment

0 commit comments

Comments
 (0)