From bcf712a69fc5d5f79a0540c7265021d3e76364fb Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 23 Oct 2024 17:22:27 -0400 Subject: [PATCH 1/4] Wrap _call_pickled() with _capture_exc() --- Lib/concurrent/futures/interpreter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/concurrent/futures/interpreter.py b/Lib/concurrent/futures/interpreter.py index fd7941adb766bb..d17688dc9d7346 100644 --- a/Lib/concurrent/futures/interpreter.py +++ b/Lib/concurrent/futures/interpreter.py @@ -107,7 +107,8 @@ def _call(cls, func, args, kwargs, resultsid): @classmethod def _call_pickled(cls, pickled, resultsid): - fn, args, kwargs = pickle.loads(pickled) + with cls._capture_exc(resultsid): + fn, args, kwargs = pickle.loads(pickled) cls._call(fn, args, kwargs, resultsid) def __init__(self, initdata, shared=None): From 238e1345822a478af43381b32ce8eddd0b80c2ed Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 23 Oct 2024 17:35:42 -0400 Subject: [PATCH 2/4] Add a test. --- .../test_interpreter_pool.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Lib/test/test_concurrent_futures/test_interpreter_pool.py b/Lib/test/test_concurrent_futures/test_interpreter_pool.py index 5264b1bb6e9c75..780f9e4caf12fa 100644 --- a/Lib/test/test_concurrent_futures/test_interpreter_pool.py +++ b/Lib/test/test_concurrent_futures/test_interpreter_pool.py @@ -56,6 +56,16 @@ def pipe(self): return r, w +class Shenanigans: + """Succeeds with pickle.dumps(), but fails with pickle.loads()""" + def __init__(self, value): + if value == 1: + raise RuntimeError("gotcha") + + def __reduce__(self): + return (self.__class__, (1,)) + + class InterpreterPoolExecutorTest( InterpretersMixin, ExecutorTest, BaseTestCase): @@ -279,6 +289,14 @@ def test_idle_thread_reuse(self): self.assertEqual(len(executor._threads), 1) executor.shutdown(wait=True) + def test_pickle_errors_propagate(self): + # GH-125864: Pickle errors happen before the script tries to execute, so the + # queue used to wait infinitely. + + fut = self.executor.submit(Shenanigans(0)) + with self.assertRaisesRegex(RuntimeError, "gotcha"): + fut.result() + class AsyncioTest(InterpretersMixin, testasyncio_utils.TestCase): @@ -351,6 +369,7 @@ def test_default_executor(self): self.assertNotEqual(interpid, unexpected) + def setUpModule(): setup_module() From 1607c8ce862b078e80e24cd9ce8686cc170c123c Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 23 Oct 2024 17:37:26 -0400 Subject: [PATCH 3/4] Remove extra newline. --- Lib/test/test_concurrent_futures/test_interpreter_pool.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_concurrent_futures/test_interpreter_pool.py b/Lib/test/test_concurrent_futures/test_interpreter_pool.py index 780f9e4caf12fa..31a420dc420281 100644 --- a/Lib/test/test_concurrent_futures/test_interpreter_pool.py +++ b/Lib/test/test_concurrent_futures/test_interpreter_pool.py @@ -369,7 +369,6 @@ def test_default_executor(self): self.assertNotEqual(interpid, unexpected) - def setUpModule(): setup_module() From 4a63ff5672114ef69d320f3dc603f3f203cb94fe Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 23 Oct 2024 19:16:27 -0400 Subject: [PATCH 4/4] Make the shenanigans more clear. --- Lib/test/test_concurrent_futures/test_interpreter_pool.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_concurrent_futures/test_interpreter_pool.py b/Lib/test/test_concurrent_futures/test_interpreter_pool.py index 31a420dc420281..ea1512fc830d0c 100644 --- a/Lib/test/test_concurrent_futures/test_interpreter_pool.py +++ b/Lib/test/test_concurrent_futures/test_interpreter_pool.py @@ -56,7 +56,7 @@ def pipe(self): return r, w -class Shenanigans: +class PickleShenanigans: """Succeeds with pickle.dumps(), but fails with pickle.loads()""" def __init__(self, value): if value == 1: @@ -293,7 +293,7 @@ def test_pickle_errors_propagate(self): # GH-125864: Pickle errors happen before the script tries to execute, so the # queue used to wait infinitely. - fut = self.executor.submit(Shenanigans(0)) + fut = self.executor.submit(PickleShenanigans(0)) with self.assertRaisesRegex(RuntimeError, "gotcha"): fut.result()