diff --git a/Lib/profiling/tracing/__init__.py b/Lib/profiling/tracing/__init__.py index a6b8edf721611f..25b67943930acd 100644 --- a/Lib/profiling/tracing/__init__.py +++ b/Lib/profiling/tracing/__init__.py @@ -197,7 +197,10 @@ def main(): # in the module's namespace. globs = module.__dict__ globs.update({ - '__spec__': spec, + # See gh-140729, set None to __spec__ according + # to the documentation, + # https://docs.python.org/3/reference/import.html#module-specs + '__spec__': None, '__file__': spec.origin, '__name__': spec.name, '__package__': None, diff --git a/Lib/test/test_profiling/test_sampling_profiler.py b/Lib/test/test_profiling/test_sampling_profiler.py index c2cc2ddd48a02c..5a23fb4e9b3b78 100644 --- a/Lib/test/test_profiling/test_sampling_profiler.py +++ b/Lib/test/test_profiling/test_sampling_profiler.py @@ -3356,5 +3356,52 @@ def worker(x): self.assertIn("Results: [2, 4, 6]", stdout) self.assertNotIn("Can't pickle", stderr) + +@requires_subprocess() +@skip_if_not_supported +class TestProcessRunSupport(unittest.TestCase): + """ + Test that ProcessPoolExecutor works correctly with profiling.sampling. + """ + + def test_process_run_pickle(self): + # gh-140729: test use ProcessPoolExecutor.map() can sampling + val = 10000 + test_script = f''' +import multiprocessing + +def worker(x): + print(__name__) + print('x =', x) + return x * 2 + +if __name__ == "__main__": + multiprocessing.set_start_method("spawn") + p = multiprocessing.Process(target=worker, args=({val},)) + p.start() + p.join() +''' + with os_helper.temp_dir() as temp_dir: + script = script_helper.make_script( + temp_dir, 'test_process_run_pickle', test_script + ) + with SuppressCrashReport(): + with script_helper.spawn_python( + "-m", "cProfile", + script, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) as proc: + try: + stdout, stderr = proc.communicate(timeout=SHORT_TIMEOUT) + except subprocess.TimeoutExpired: + proc.kill() + stdout, stderr = proc.communicate() + + self.assertIn(f"x = {val}", stdout) + self.assertIn("__mp_main__", stdout) + self.assertNotIn("Can't pickle", stderr) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2026-02-11-09-08-59.gh-issue-140729.eh_105.rst b/Misc/NEWS.d/next/Library/2026-02-11-09-08-59.gh-issue-140729.eh_105.rst new file mode 100644 index 00000000000000..ecbdde092c382a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-02-11-09-08-59.gh-issue-140729.eh_105.rst @@ -0,0 +1,3 @@ +Fix pickling error in the cProfile module when using ``multiprocessing.Process`` +or ``concurrent.futures.ProcessPoolExecutor`` script, which can not be +properly pickled and executed.