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.
This commit is contained in:
Gregory P. Smith 2026-01-12 15:47:21 -08:00 committed by GitHub
parent d5882c5b70
commit 298d5440eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 52 additions and 1 deletions

View file

@ -152,6 +152,8 @@ def ensure_running(self):
main_kws['sys_path'] = data['sys_path']
if 'init_main_from_path' in data:
main_kws['main_path'] = data['init_main_from_path']
if 'sys_argv' in data:
main_kws['sys_argv'] = data['sys_argv']
with socket.socket(socket.AF_UNIX) as listener:
address = connection.arbitrary_address('AF_UNIX')
@ -197,7 +199,7 @@ def ensure_running(self):
#
def main(listener_fd, alive_r, preload, main_path=None, sys_path=None,
*, authkey_r=None):
*, sys_argv=None, authkey_r=None):
"""Run forkserver."""
if authkey_r is not None:
try:
@ -209,6 +211,8 @@ def main(listener_fd, alive_r, preload, main_path=None, sys_path=None,
authkey = b''
if preload:
if sys_argv is not None:
sys.argv[:] = sys_argv
if sys_path is not None:
sys.path[:] = sys_path
if '__main__' in preload and main_path is not None:

View file

@ -7086,6 +7086,26 @@ def test_preload_main(self):
out = out.decode().split("\n")
self.assertEqual(out, ['__main__', '__mp_main__', 'f', 'f', ''])
def test_preload_main_sys_argv(self):
# gh-143706: Check that sys.argv is set before __main__ is pre-loaded
if multiprocessing.get_start_method() != "forkserver":
self.skipTest("forkserver specific test")
name = os.path.join(os.path.dirname(__file__), 'mp_preload_sysargv.py')
_, out, err = test.support.script_helper.assert_python_ok(
name, 'foo', 'bar')
self.assertEqual(err, b'')
out = out.decode().split("\n")
expected_argv = "['foo', 'bar']"
self.assertEqual(out, [
f"module:{expected_argv}",
f"fun:{expected_argv}",
f"module:{expected_argv}",
f"fun:{expected_argv}",
'',
])
#
# Mixins
#

View file

@ -0,0 +1,22 @@
# gh-143706: Test that sys.argv is correctly set during main module import
# when using forkserver with __main__ preloading.
import multiprocessing
import sys
# This will be printed during module import - sys.argv should be correct here
print(f"module:{sys.argv[1:]}")
def fun():
# This will be printed when the function is called
print(f"fun:{sys.argv[1:]}")
if __name__ == "__main__":
ctx = multiprocessing.get_context("forkserver")
ctx.set_forkserver_preload(['__main__'])
fun()
p = ctx.Process(target=fun)
p.start()
p.join()

View file

@ -0,0 +1,5 @@
Fix :mod:`multiprocessing` forkserver so that :data:`sys.argv` is correctly
set before ``__main__`` is preloaded. Previously, :data:`sys.argv` was empty
during main module import in forkserver child processes. This fixes a
regression introduced in 3.13.8 and 3.14.1. Root caused by Aaron Wieczorek,
test provided by Thomas Watson, thanks!