Skip to content

Commit 480f480

Browse files
authored
[3.13] gh-143706: Fix sys.argv not set during multiprocessing forkserver __main__ preload (GH-143717) (#143821)
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. (cherry picked from commit 298d544)
1 parent b6c6c47 commit 480f480

File tree

4 files changed

+53
-1
lines changed

4 files changed

+53
-1
lines changed

Lib/multiprocessing/forkserver.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ def ensure_running(self):
134134
main_kws['sys_path'] = data['sys_path']
135135
if 'init_main_from_path' in data:
136136
main_kws['main_path'] = data['init_main_from_path']
137+
if 'sys_argv' in data:
138+
main_kws['sys_argv'] = data['sys_argv']
137139

138140
with socket.socket(socket.AF_UNIX) as listener:
139141
address = connection.arbitrary_address('AF_UNIX')
@@ -166,9 +168,12 @@ def ensure_running(self):
166168
#
167169
#
168170

169-
def main(listener_fd, alive_r, preload, main_path=None, sys_path=None):
171+
def main(listener_fd, alive_r, preload, main_path=None, sys_path=None,
172+
*, sys_argv=None):
170173
'''Run forkserver.'''
171174
if preload:
175+
if sys_argv is not None:
176+
sys.argv[:] = sys_argv
172177
if sys_path is not None:
173178
sys.path[:] = sys_path
174179
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
@@ -6584,6 +6584,26 @@ def test_preload_main(self):
65846584
out = out.decode().split("\n")
65856585
self.assertEqual(out, ['__main__', '__mp_main__', 'f', 'f', ''])
65866586

6587+
def test_preload_main_sys_argv(self):
6588+
# gh-143706: Check that sys.argv is set before __main__ is pre-loaded
6589+
if multiprocessing.get_start_method() != "forkserver":
6590+
self.skipTest("forkserver specific test")
6591+
6592+
name = os.path.join(os.path.dirname(__file__), 'mp_preload_sysargv.py')
6593+
_, out, err = test.support.script_helper.assert_python_ok(
6594+
name, 'foo', 'bar')
6595+
self.assertEqual(err, b'')
6596+
6597+
out = out.decode().split("\n")
6598+
expected_argv = "['foo', 'bar']"
6599+
self.assertEqual(out, [
6600+
f"module:{expected_argv}",
6601+
f"fun:{expected_argv}",
6602+
f"module:{expected_argv}",
6603+
f"fun:{expected_argv}",
6604+
'',
6605+
])
6606+
65876607
#
65886608
# Mixins
65896609
#

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)