Skip to content

Commit

Permalink
gh-98360: multiprocessing now spawns children on Windows with correct…
Browse files Browse the repository at this point in the history
… argv[0] in virtual environments (GH-98462)

(cherry picked from commit e48f9b2)

Co-authored-by: Steve Dower <steve.dower@python.org>
  • Loading branch information
miss-islington and zooba committed Oct 20, 2022
1 parent 49d7993 commit ace6611
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 3 deletions.
5 changes: 3 additions & 2 deletions Lib/multiprocessing/popen_spawn_win32.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,20 @@ def __init__(self, process_obj):
wfd = msvcrt.open_osfhandle(whandle, 0)
cmd = spawn.get_command_line(parent_pid=os.getpid(),
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
cmd[0] = python_exe = sys._base_executable
env = os.environ.copy()
env["__PYVENV_LAUNCHER__"] = sys.executable
else:
env = None

cmd = ' '.join('"%s"' % x for x in cmd)

with open(wfd, 'wb', closefd=True) as to_child:
# start process
try:
Expand Down
40 changes: 40 additions & 0 deletions Lib/test/_test_venv_multiprocessing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import multiprocessing
import random
import sys
import time

def fill_queue(queue, code):
queue.put(code)


def drain_queue(queue, code):
if code != queue.get():
sys.exit(1)


def test_func():
code = random.randrange(0, 1000)
queue = multiprocessing.Queue()
fill_pool = multiprocessing.Process(
target=fill_queue,
args=(queue, code)
)
drain_pool = multiprocessing.Process(
target=drain_queue,
args=(queue, code)
)
drain_pool.start()
fill_pool.start()
fill_pool.join()
drain_pool.join()


def main():
test_pool = multiprocessing.Process(target=test_func)
test_pool.start()
test_pool.join()
sys.exit(test_pool.exitcode)


if __name__ == "__main__":
main()
16 changes: 15 additions & 1 deletion Lib/test/test_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from test.support import (captured_stdout, captured_stderr, requires_zlib,
skip_if_broken_multiprocessing_synchronize, verbose,
requires_subprocess, is_emscripten, is_wasi,
requires_venv_with_pip)
requires_venv_with_pip, TEST_HOME_DIR)
from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree)
import unittest
import venv
Expand Down Expand Up @@ -482,6 +482,20 @@ def test_multiprocessing(self):
'pool.terminate()'])
self.assertEqual(out.strip(), "python".encode())

@requireVenvCreate
def test_multiprocessing_recursion(self):
"""
Test that the multiprocessing is able to spawn itself
"""
skip_if_broken_multiprocessing_synchronize()

rmtree(self.env_dir)
self.run_with_capture(venv.create, self.env_dir)
envpy = os.path.join(os.path.realpath(self.env_dir),
self.bindir, self.exe)
script = os.path.join(TEST_HOME_DIR, '_test_venv_multiprocessing.py')
subprocess.check_call([envpy, script])

@unittest.skipIf(os.name == 'nt', 'not relevant on Windows')
def test_deactivate_with_strict_bash_opts(self):
bash = shutil.which("bash")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fixes :mod:`multiprocessing` spawning child processes on Windows from a
virtual environment to ensure that child processes that also use
:mod:`multiprocessing` to spawn more children will recognize that they are
in a virtual environment.

0 comments on commit ace6611

Please sign in to comment.