]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-126631: gh-137996: fix pre-loading of `__main__` (GH-135295) (#138609)
authorGregory P. Smith <68491+gpshead@users.noreply.github.com>
Mon, 8 Sep 2025 18:13:23 +0000 (11:13 -0700)
committerGitHub <noreply@github.com>
Mon, 8 Sep 2025 18:13:23 +0000 (11:13 -0700)
gh-126631: gh-137996: fix pre-loading of `__main__`

The `main_path` parameter was renamed `init_main_from_name`, update the
forkserver code accordingly.  This was leading to slower startup times when people
were trying to preload the main module.

---------
(cherry picked from commit 0912b3a6dbd226bea79eb8d70d5a06230804d4cb)

Co-authored-by: Duane Griffin <duaneg@dghda.com>
Lib/multiprocessing/forkserver.py
Lib/test/_test_multiprocessing.py
Lib/test/mp_preload_main.py [new file with mode: 0644]
Misc/NEWS.d/next/Library/2025-06-10-21-00-48.gh-issue-126631.eITVJd.rst [new file with mode: 0644]

index 0c84c0baa2a099aab416b98a2c2f14c233f59205..e243442e7a15bd80c7a424d12c50df65936e02f6 100644 (file)
@@ -127,12 +127,13 @@ class ForkServer(object):
             cmd = ('from multiprocessing.forkserver import main; ' +
                    'main(%d, %d, %r, **%r)')
 
+            main_kws = {}
             if self._preload_modules:
-                desired_keys = {'main_path', 'sys_path'}
                 data = spawn.get_preparation_data('ignore')
-                data = {x: y for x, y in data.items() if x in desired_keys}
-            else:
-                data = {}
+                if 'sys_path' in data:
+                    main_kws['sys_path'] = data['sys_path']
+                if 'init_main_from_path' in data:
+                    main_kws['main_path'] = data['init_main_from_path']
 
             with socket.socket(socket.AF_UNIX) as listener:
                 address = connection.arbitrary_address('AF_UNIX')
@@ -147,7 +148,7 @@ class ForkServer(object):
                 try:
                     fds_to_pass = [listener.fileno(), alive_r]
                     cmd %= (listener.fileno(), alive_r, self._preload_modules,
-                            data)
+                            main_kws)
                     exe = spawn.get_executable()
                     args = [exe] + util._args_from_interpreter_flags()
                     args += ['-c', cmd]
index 22e19871e50d2581cc31cb77f45bc8e6cc6ff2b5..c22ce769c48963ef5b55c6972d13d77e7990adb4 100644 (file)
@@ -6545,6 +6545,18 @@ class MiscTestCase(unittest.TestCase):
         self.assertEqual(q.get_nowait(), "done")
         close_queue(q)
 
+    def test_preload_main(self):
+        # gh-126631: Check that __main__ can be pre-loaded
+        if multiprocessing.get_start_method() != "forkserver":
+            self.skipTest("forkserver specific test")
+
+        name = os.path.join(os.path.dirname(__file__), 'mp_preload_main.py')
+        _, out, err = test.support.script_helper.assert_python_ok(name)
+        self.assertEqual(err, b'')
+
+        # The trailing empty string comes from split() on output ending with \n
+        out = out.decode().split("\n")
+        self.assertEqual(out, ['__main__', '__mp_main__', 'f', 'f', ''])
 
 #
 # Mixins
diff --git a/Lib/test/mp_preload_main.py b/Lib/test/mp_preload_main.py
new file mode 100644 (file)
index 0000000..acb3428
--- /dev/null
@@ -0,0 +1,14 @@
+import multiprocessing
+
+print(f"{__name__}")
+
+def f():
+    print("f")
+
+if __name__ == "__main__":
+    ctx = multiprocessing.get_context("forkserver")
+    ctx.set_forkserver_preload(['__main__'])
+    for _ in range(2):
+        p = ctx.Process(target=f)
+        p.start()
+        p.join()
diff --git a/Misc/NEWS.d/next/Library/2025-06-10-21-00-48.gh-issue-126631.eITVJd.rst b/Misc/NEWS.d/next/Library/2025-06-10-21-00-48.gh-issue-126631.eITVJd.rst
new file mode 100644 (file)
index 0000000..195253b
--- /dev/null
@@ -0,0 +1,2 @@
+Fix :mod:`multiprocessing` ``forkserver`` bug which prevented ``__main__``
+from being preloaded.