From: Gregory P. Smith <68491+gpshead@users.noreply.github.com> Date: Mon, 12 Jan 2026 23:47:21 +0000 (-0800) Subject: gh-143706: Fix sys.argv not set during multiprocessing forkserver `__main__` preload... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=298d5440eb83f2dfd5651bac86d1592ec358d54c;p=thirdparty%2FPython%2Fcpython.git 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. --- diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py index cc8947c5e04f..15c455a598dc 100644 --- a/Lib/multiprocessing/forkserver.py +++ b/Lib/multiprocessing/forkserver.py @@ -152,6 +152,8 @@ class ForkServer(object): 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 @@ class ForkServer(object): # 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: diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 844539104e3a..cc07062eee6f 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -7086,6 +7086,26 @@ class MiscTestCase(unittest.TestCase): 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 # diff --git a/Lib/test/mp_preload_sysargv.py b/Lib/test/mp_preload_sysargv.py new file mode 100644 index 000000000000..5ad38cda889f --- /dev/null +++ b/Lib/test/mp_preload_sysargv.py @@ -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() diff --git a/Misc/NEWS.d/next/Library/2026-01-12-07-17-38.gh-issue-143706.sysArgv.rst b/Misc/NEWS.d/next/Library/2026-01-12-07-17-38.gh-issue-143706.sysArgv.rst new file mode 100644 index 000000000000..5bdefbb7913c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-12-07-17-38.gh-issue-143706.sysArgv.rst @@ -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!