https://github.com/python/cpython/commit/933c6653cba235b3af2250bb19713694b560c367
commit: 933c6653cba235b3af2250bb19713694b560c367
branch: main
author: Duprat <[email protected]>
committer: picnixz <[email protected]>
date: 2025-04-08T15:11:13Z
summary:
gh-132063: ensure that `ProcessPoolExecutor` does not swallow falsey exceptions
(#132129)
files:
A Misc/NEWS.d/next/Library/2025-04-05-15-05-09.gh-issue-132063.KHnslU.rst
M Lib/concurrent/futures/_base.py
M Lib/concurrent/futures/process.py
M Lib/test/test_concurrent_futures/executor.py
diff --git a/Lib/concurrent/futures/_base.py b/Lib/concurrent/futures/_base.py
index d5ba39e3d71774..d98b1ebdd584b5 100644
--- a/Lib/concurrent/futures/_base.py
+++ b/Lib/concurrent/futures/_base.py
@@ -390,7 +390,7 @@ def done(self):
return self._state in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED]
def __get_result(self):
- if self._exception:
+ if self._exception is not None:
try:
raise self._exception
finally:
diff --git a/Lib/concurrent/futures/process.py
b/Lib/concurrent/futures/process.py
index 4847550908adab..76b7b2abe836d8 100644
--- a/Lib/concurrent/futures/process.py
+++ b/Lib/concurrent/futures/process.py
@@ -440,7 +440,7 @@ def process_result_item(self, result_item):
work_item = self.pending_work_items.pop(result_item.work_id, None)
# work_item can be None if another process terminated (see above)
if work_item is not None:
- if result_item.exception:
+ if result_item.exception is not None:
work_item.future.set_exception(result_item.exception)
else:
work_item.future.set_result(result_item.result)
diff --git a/Lib/test/test_concurrent_futures/executor.py
b/Lib/test/test_concurrent_futures/executor.py
index d88c34d1c8c8e4..95bf8fcd25bf54 100644
--- a/Lib/test/test_concurrent_futures/executor.py
+++ b/Lib/test/test_concurrent_futures/executor.py
@@ -24,6 +24,21 @@ def make_dummy_object(_):
return MyObject()
+# Used in test_swallows_falsey_exceptions
+def raiser(exception, msg='std'):
+ raise exception(msg)
+
+
+class FalseyBoolException(Exception):
+ def __bool__(self):
+ return False
+
+
+class FalseyLenException(Exception):
+ def __len__(self):
+ return 0
+
+
class ExecutorTest:
# Executor.shutdown() and context manager usage is tested by
@@ -205,3 +220,16 @@ def test_free_reference(self):
for _ in support.sleeping_retry(support.SHORT_TIMEOUT):
if wr() is None:
break
+
+ def test_swallows_falsey_exceptions(self):
+ # see gh-132063: Prevent exceptions that evaluate as falsey
+ # from being ignored.
+ # Recall: `x` is falsey if `len(x)` returns 0 or `bool(x)` returns
False.
+
+ msg = 'boolbool'
+ with self.assertRaisesRegex(FalseyBoolException, msg):
+ self.executor.submit(raiser, FalseyBoolException, msg).result()
+
+ msg = 'lenlen'
+ with self.assertRaisesRegex(FalseyLenException, msg):
+ self.executor.submit(raiser, FalseyLenException, msg).result()
diff --git
a/Misc/NEWS.d/next/Library/2025-04-05-15-05-09.gh-issue-132063.KHnslU.rst
b/Misc/NEWS.d/next/Library/2025-04-05-15-05-09.gh-issue-132063.KHnslU.rst
new file mode 100644
index 00000000000000..d3761759772d03
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-04-05-15-05-09.gh-issue-132063.KHnslU.rst
@@ -0,0 +1,2 @@
+Prevent exceptions that evaluate as falsey (namely, when their ``__bool__``
method returns ``False`` or their ``__len__`` method returns 0)
+from being ignored by :class:`concurrent.futures.ProcessPoolExecutor` and
:class:`concurrent.futures.ThreadPoolExecutor`.
_______________________________________________
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]