https://github.com/python/cpython/commit/994ab5c922b179ab1884f05b3440c24db9e9733d
commit: 994ab5c922b179ab1884f05b3440c24db9e9733d
branch: main
author: yihong <[email protected]>
committer: pablogsal <[email protected]>
date: 2025-11-17T12:43:14Z
summary:
gh-140729: Add __mp_main__ as a duplicate for __main__ for pickle to work
(#140735)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2025-10-29-11-31-59.gh-issue-140729.t9JsNt.rst
M Lib/profiling/sampling/_sync_coordinator.py
M Lib/test/test_profiling/test_sampling_profiler.py
diff --git a/Lib/profiling/sampling/_sync_coordinator.py
b/Lib/profiling/sampling/_sync_coordinator.py
index adb040e89cc7b1..be63dbe3e904ce 100644
--- a/Lib/profiling/sampling/_sync_coordinator.py
+++ b/Lib/profiling/sampling/_sync_coordinator.py
@@ -10,6 +10,7 @@
import socket
import runpy
import time
+import types
from typing import List, NoReturn
@@ -175,15 +176,21 @@ def _execute_script(script_path: str, script_args:
List[str], cwd: str) -> None:
try:
with open(script_path, 'rb') as f:
source_code = f.read()
+
except FileNotFoundError as e:
raise TargetError(f"Script file not found: {script_path}") from e
except PermissionError as e:
raise TargetError(f"Permission denied reading script: {script_path}")
from e
try:
- # Compile and execute the script
+ main_module = types.ModuleType("__main__")
+ main_module.__file__ = script_path
+ main_module.__builtins__ = __builtins__
+ # gh-140729: Create a __mp_main__ module to allow pickling
+ sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
+
code = compile(source_code, script_path, 'exec', module='__main__')
- exec(code, {'__name__': '__main__', '__file__': script_path})
+ exec(code, main_module.__dict__)
except SyntaxError as e:
raise TargetError(f"Syntax error in script {script_path}: {e}") from e
except SystemExit:
diff --git a/Lib/test/test_profiling/test_sampling_profiler.py
b/Lib/test/test_profiling/test_sampling_profiler.py
index 5b924cb24531b6..0ba6799a1ce5ba 100644
--- a/Lib/test/test_profiling/test_sampling_profiler.py
+++ b/Lib/test/test_profiling/test_sampling_profiler.py
@@ -22,7 +22,13 @@
from profiling.sampling.gecko_collector import GeckoCollector
from test.support.os_helper import unlink
-from test.support import force_not_colorized_test_class, SHORT_TIMEOUT
+from test.support import (
+ force_not_colorized_test_class,
+ SHORT_TIMEOUT,
+ script_helper,
+ os_helper,
+ SuppressCrashReport,
+)
from test.support.socket_helper import find_unused_port
from test.support import requires_subprocess, is_emscripten
from test.support import captured_stdout, captured_stderr
@@ -3009,5 +3015,49 @@ def test_parse_mode_function(self):
profiling.sampling.sample._parse_mode("invalid")
+@requires_subprocess()
+@skip_if_not_supported
+class TestProcessPoolExecutorSupport(unittest.TestCase):
+ """
+ Test that ProcessPoolExecutor works correctly with profiling.sampling.
+ """
+
+ def test_process_pool_executor_pickle(self):
+ # gh-140729: test use ProcessPoolExecutor.map() can sampling
+ test_script = '''
+import concurrent.futures
+
+def worker(x):
+ return x * 2
+
+if __name__ == "__main__":
+ with concurrent.futures.ProcessPoolExecutor() as executor:
+ results = list(executor.map(worker, [1, 2, 3]))
+ print(f"Results: {results}")
+'''
+ with os_helper.temp_dir() as temp_dir:
+ script = script_helper.make_script(
+ temp_dir, 'test_process_pool_executor_pickle', test_script
+ )
+ with SuppressCrashReport():
+ with script_helper.spawn_python(
+ "-m", "profiling.sampling.sample",
+ "-d", "5",
+ "-i", "100000",
+ script,
+ stderr=subprocess.PIPE,
+ text=True
+ ) as proc:
+ proc.wait(timeout=SHORT_TIMEOUT)
+ stdout = proc.stdout.read()
+ stderr = proc.stderr.read()
+
+ if "PermissionError" in stderr:
+ self.skipTest("Insufficient permissions for remote profiling")
+
+ self.assertIn("Results: [2, 4, 6]", stdout)
+ self.assertNotIn("Can't pickle", stderr)
+
+
if __name__ == "__main__":
unittest.main()
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-29-11-31-59.gh-issue-140729.t9JsNt.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-29-11-31-59.gh-issue-140729.t9JsNt.rst
new file mode 100644
index 00000000000000..6725547667fb3c
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-29-11-31-59.gh-issue-140729.t9JsNt.rst
@@ -0,0 +1,2 @@
+Fix pickling error in the sampling profiler when using
``concurrent.futures.ProcessPoolExecutor``
+script can not be properly pickled and executed in worker processes.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]