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)
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
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()
--- /dev/null
+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`.