Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.11] gh-98360: multiprocessing now spawns children on Windows with correct argv[0] in virtual environments (GH-98462) #98490

Merged
merged 1 commit into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.