Skip to content

Commit 298d544

Browse files
authored
gh-143706: Fix sys.argv not set during multiprocessing forkserver __main__ preload (#143717)
The forkserver was not passing sys.argv to its main() function, causing sys.argv to be empty during `__main__` module import in child processes. This fixes a non-obvious regression inadvertently introduced by the gh-126631 main preloading fix.
1 parent d5882c5 commit 298d544

File tree

4 files changed

+52
-1
lines changed

4 files changed

+52
-1
lines changed

Lib/multiprocessing/forkserver.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ def ensure_running(self):
152152
main_kws['sys_path'] = data['sys_path']
153153
if 'init_main_from_path' in data:
154154
main_kws['main_path'] = data['init_main_from_path']
155+
if 'sys_argv' in data:
156+
main_kws['sys_argv'] = data['sys_argv']
155157

156158
with socket.socket(socket.AF_UNIX) as listener:
157159
address = connection.arbitrary_address('AF_UNIX')
@@ -197,7 +199,7 @@ def ensure_running(self):
197199
#
198200

199201
def main(listener_fd, alive_r, preload, main_path=None, sys_path=None,
200-
*, authkey_r=None):
202+
*, sys_argv=None, authkey_r=None):
201203
"""Run forkserver."""
202204
if authkey_r is not None:
203205
try:
@@ -209,6 +211,8 @@ def main(listener_fd, alive_r, preload, main_path=None, sys_path=None,
209211
authkey = b''
210212

211213
if preload:
214+
if sys_argv is not None:
215+
sys.argv[:] = sys_argv
212216
if sys_path is not None:
213217
sys.path[:] = sys_path
214218
if '__main__' in preload and main_path is not None:

Lib/test/_test_multiprocessing.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7086,6 +7086,26 @@ def test_preload_main(self):
70867086
out = out.decode().split("\n")
70877087
self.assertEqual(out, ['__main__', '__mp_main__', 'f', 'f', ''])
70887088

7089+
def test_preload_main_sys_argv(self):
7090+
# gh-143706: Check that sys.argv is set before __main__ is pre-loaded
7091+
if multiprocessing.get_start_method() != "forkserver":
7092+
self.skipTest("forkserver specific test")
7093+
7094+
name = os.path.join(os.path.dirname(__file__), 'mp_preload_sysargv.py')
7095+
_, out, err = test.support.script_helper.assert_python_ok(
7096+
name, 'foo', 'bar')
7097+
self.assertEqual(err, b'')
7098+
7099+
out = out.decode().split("\n")
7100+
expected_argv = "['foo', 'bar']"
7101+
self.assertEqual(out, [
7102+
f"module:{expected_argv}",
7103+
f"fun:{expected_argv}",
7104+
f"module:{expected_argv}",
7105+
f"fun:{expected_argv}",
7106+
'',
7107+
])
7108+
70897109
#
70907110
# Mixins
70917111
#

Lib/test/mp_preload_sysargv.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# gh-143706: Test that sys.argv is correctly set during main module import
2+
# when using forkserver with __main__ preloading.
3+
4+
import multiprocessing
5+
import sys
6+
7+
# This will be printed during module import - sys.argv should be correct here
8+
print(f"module:{sys.argv[1:]}")
9+
10+
def fun():
11+
# This will be printed when the function is called
12+
print(f"fun:{sys.argv[1:]}")
13+
14+
if __name__ == "__main__":
15+
ctx = multiprocessing.get_context("forkserver")
16+
ctx.set_forkserver_preload(['__main__'])
17+
18+
fun()
19+
20+
p = ctx.Process(target=fun)
21+
p.start()
22+
p.join()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix :mod:`multiprocessing` forkserver so that :data:`sys.argv` is correctly
2+
set before ``__main__`` is preloaded. Previously, :data:`sys.argv` was empty
3+
during main module import in forkserver child processes. This fixes a
4+
regression introduced in 3.13.8 and 3.14.1. Root caused by Aaron Wieczorek,
5+
test provided by Thomas Watson, thanks!

0 commit comments

Comments
 (0)