From 9efa479e5b4727b52572796c2e25e81d9e2aee67 Mon Sep 17 00:00:00 2001
From: "Yves.Duprat" <yduprat@gmail.com>
Date: Fri, 4 Apr 2025 21:22:31 +0200
Subject: [PATCH 1/9] Initial commit

---
 Lib/concurrent/futures/_base.py                    |  2 +-
 Lib/concurrent/futures/process.py                  |  2 +-
 .../test_concurrent_futures/test_process_pool.py   | 14 ++++++++++++++
 .../test_concurrent_futures/test_thread_pool.py    | 13 +++++++++++++
 4 files changed, 29 insertions(+), 2 deletions(-)

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/test_process_pool.py b/Lib/test/test_concurrent_futures/test_process_pool.py
index 3f13a1900a4ca4..c2323b610f5f3e 100644
--- a/Lib/test/test_concurrent_futures/test_process_pool.py
+++ b/Lib/test/test_concurrent_futures/test_process_pool.py
@@ -337,6 +337,20 @@ def test_force_shutdown_workers_stops_pool(self, function_name):
                 if not worker_process.is_alive():
                     break
 
+    class MyException(Exception):
+        def __bool__(self):
+            return False
+
+    @classmethod
+    def raiser(cls):
+        raise cls.MyException("foo")
+
+    def test_swallows_falsy_exceptions(self):
+        # fix gh-132063 issue
+        with self.assertRaisesRegex(self.MyException, "foo"):
+            with self.executor_type(max_workers=1) as executor:
+                executor.submit(self.raiser).result()
+
 
 create_executor_tests(globals(), ProcessPoolExecutorTest,
                       executor_mixins=(ProcessPoolForkMixin,
diff --git a/Lib/test/test_concurrent_futures/test_thread_pool.py b/Lib/test/test_concurrent_futures/test_thread_pool.py
index 4324241b374967..d85db59693badf 100644
--- a/Lib/test/test_concurrent_futures/test_thread_pool.py
+++ b/Lib/test/test_concurrent_futures/test_thread_pool.py
@@ -112,6 +112,19 @@ def log_n_wait(ident):
         # ident='third' is cancelled because it remained in the collection of futures
         self.assertListEqual(log, ["ident='first' started", "ident='first' stopped"])
 
+    class MyException(Exception):
+        def __bool__(self):
+            return False
+
+    @classmethod
+    def raiser(cls):
+        raise cls.MyException("foo")
+
+    def test_swallows_falsy_exceptions(self):
+        # fix gh-132063 issue
+        with self.assertRaisesRegex(self.MyException, "foo"):
+            with self.executor_type(max_workers=1) as executor:
+                executor.submit(self.raiser).result()
 
 def setUpModule():
     setup_module()

From 8295a27f66bc9542aa7fd4303dbdc871f8adcdb7 Mon Sep 17 00:00:00 2001
From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com>
Date: Sat, 5 Apr 2025 15:05:12 +0000
Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?=
 =?UTF-8?q?rb=5Fit.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../next/Library/2025-04-05-15-05-09.gh-issue-132063.KHnslU.rst  | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 Misc/NEWS.d/next/Library/2025-04-05-15-05-09.gh-issue-132063.KHnslU.rst

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..61d1e2d20950bf
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-04-05-15-05-09.gh-issue-132063.KHnslU.rst
@@ -0,0 +1 @@
+Fix tests of exception attribute in `:method:process_result_item`of `:class:_ExecutorManagerThread` and `:method:__get_result` of `:class:Future` in module `:lib:concurrent`.

From 81ec6588f2e369f616c56828a3d0f9b89335f32b Mon Sep 17 00:00:00 2001
From: Duprat <yduprat@gmail.com>
Date: Sat, 5 Apr 2025 17:07:17 +0200
Subject: [PATCH 3/9] Update news

---
 .../next/Library/2025-04-05-15-05-09.gh-issue-132063.KHnslU.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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
index 61d1e2d20950bf..01831eff183258 100644
--- 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
@@ -1 +1 @@
-Fix tests of exception attribute in `:method:process_result_item`of `:class:_ExecutorManagerThread` and `:method:__get_result` of `:class:Future` in module `:lib:concurrent`.
+Fix tests of exception attribute in :method:`process_result_item`of :class:`_ExecutorManagerThread` and :method:`__get_result` of :class:`Future` in module :lib:`concurrent`.

From 098ed59ecdefea0ab4fe2bd806d9456237d4edcc Mon Sep 17 00:00:00 2001
From: Duprat <yduprat@gmail.com>
Date: Tue, 8 Apr 2025 12:31:54 +0200
Subject: [PATCH 4/9] Update news

---
 .../Library/2025-04-05-15-05-09.gh-issue-132063.KHnslU.rst     | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

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
index 01831eff183258..1501991478d7f6 100644
--- 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
@@ -1 +1,2 @@
-Fix tests of exception attribute in :method:`process_result_item`of :class:`_ExecutorManagerThread` and :method:`__get_result` of :class:`Future` in module :lib:`concurrent`.
+Prevent exceptions, that evaluate as falsey (When `__bool__` method returns `False', when `__len__` method returns 0)
+from being ignored by :class:`concurrent.futures.ProcessPoolExecutor` and :class:`concurrent.futures.ThreadPoolExecutor`.

From bcf251083244d57ceae60813c176105c6fef38f7 Mon Sep 17 00:00:00 2001
From: "Yves.Duprat" <yduprat@gmail.com>
Date: Tue, 8 Apr 2025 12:25:56 +0200
Subject: [PATCH 5/9] Move test to ExecutorTest and Updates the comment of test

---
 Lib/test/test_concurrent_futures/executor.py  | 26 +++++++++++++++++++
 .../test_process_pool.py                      | 14 ----------
 .../test_thread_pool.py                       | 13 ----------
 3 files changed, 26 insertions(+), 27 deletions(-)

diff --git a/Lib/test/test_concurrent_futures/executor.py b/Lib/test/test_concurrent_futures/executor.py
index d88c34d1c8c8e4..853771a9f573ca 100644
--- a/Lib/test/test_concurrent_futures/executor.py
+++ b/Lib/test/test_concurrent_futures/executor.py
@@ -23,6 +23,19 @@ def my_method(self):
 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:
 
@@ -205,3 +218,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.
+        # Falsey exceptions return 0 when `__len__` method is called,
+        # False when `__bool__` method is called.
+
+        msg = 'lenlen'
+        with self.assertRaisesRegex(FalseyLenException, msg):
+            self.executor.submit(raiser, FalseyLenException, msg).result()
+        msg = 'boolbool'
+        with self.assertRaisesRegex(FalseyBoolException, msg):
+            self.executor.submit(raiser, FalseyBoolException, msg).result()
diff --git a/Lib/test/test_concurrent_futures/test_process_pool.py b/Lib/test/test_concurrent_futures/test_process_pool.py
index c2323b610f5f3e..3f13a1900a4ca4 100644
--- a/Lib/test/test_concurrent_futures/test_process_pool.py
+++ b/Lib/test/test_concurrent_futures/test_process_pool.py
@@ -337,20 +337,6 @@ def test_force_shutdown_workers_stops_pool(self, function_name):
                 if not worker_process.is_alive():
                     break
 
-    class MyException(Exception):
-        def __bool__(self):
-            return False
-
-    @classmethod
-    def raiser(cls):
-        raise cls.MyException("foo")
-
-    def test_swallows_falsy_exceptions(self):
-        # fix gh-132063 issue
-        with self.assertRaisesRegex(self.MyException, "foo"):
-            with self.executor_type(max_workers=1) as executor:
-                executor.submit(self.raiser).result()
-
 
 create_executor_tests(globals(), ProcessPoolExecutorTest,
                       executor_mixins=(ProcessPoolForkMixin,
diff --git a/Lib/test/test_concurrent_futures/test_thread_pool.py b/Lib/test/test_concurrent_futures/test_thread_pool.py
index d85db59693badf..4324241b374967 100644
--- a/Lib/test/test_concurrent_futures/test_thread_pool.py
+++ b/Lib/test/test_concurrent_futures/test_thread_pool.py
@@ -112,19 +112,6 @@ def log_n_wait(ident):
         # ident='third' is cancelled because it remained in the collection of futures
         self.assertListEqual(log, ["ident='first' started", "ident='first' stopped"])
 
-    class MyException(Exception):
-        def __bool__(self):
-            return False
-
-    @classmethod
-    def raiser(cls):
-        raise cls.MyException("foo")
-
-    def test_swallows_falsy_exceptions(self):
-        # fix gh-132063 issue
-        with self.assertRaisesRegex(self.MyException, "foo"):
-            with self.executor_type(max_workers=1) as executor:
-                executor.submit(self.raiser).result()
 
 def setUpModule():
     setup_module()

From fbbe501537f2ba92c2cfa49e315c7214172f45dd Mon Sep 17 00:00:00 2001
From: Duprat <yduprat@gmail.com>
Date: Tue, 8 Apr 2025 12:38:10 +0200
Subject: [PATCH 6/9] fix nits in news

---
 .../next/Library/2025-04-05-15-05-09.gh-issue-132063.KHnslU.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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
index 1501991478d7f6..59333a0b18e7d6 100644
--- 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
@@ -1,2 +1,2 @@
-Prevent exceptions, that evaluate as falsey (When `__bool__` method returns `False', when `__len__` method returns 0)
+Prevent exceptions, that evaluate as falsey (When ``__bool__`` method returns ``False``, when ``__len__`` method returns 0)
 from being ignored by :class:`concurrent.futures.ProcessPoolExecutor` and :class:`concurrent.futures.ThreadPoolExecutor`.

From e450739bea3edc9512f2d1d9f485d3503c614098 Mon Sep 17 00:00:00 2001
From: Duprat <yduprat@gmail.com>
Date: Tue, 8 Apr 2025 14:18:48 +0200
Subject: [PATCH 7/9] Update news
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
---
 .../next/Library/2025-04-05-15-05-09.gh-issue-132063.KHnslU.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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
index 59333a0b18e7d6..d3761759772d03 100644
--- 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
@@ -1,2 +1,2 @@
-Prevent exceptions, that evaluate as falsey (When ``__bool__`` method returns ``False``, when ``__len__`` method returns 0)
+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`.

From ea66f0b0e2aa6580f9823498abc19c21b8ee84de Mon Sep 17 00:00:00 2001
From: Duprat <yduprat@gmail.com>
Date: Tue, 8 Apr 2025 14:20:13 +0200
Subject: [PATCH 8/9] Add a blank line
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
---
 Lib/test/test_concurrent_futures/executor.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Lib/test/test_concurrent_futures/executor.py b/Lib/test/test_concurrent_futures/executor.py
index 853771a9f573ca..36f9d26287514b 100644
--- a/Lib/test/test_concurrent_futures/executor.py
+++ b/Lib/test/test_concurrent_futures/executor.py
@@ -27,6 +27,7 @@ def make_dummy_object(_):
 def raiser(exception, msg='std'):
     raise exception(msg)
 
+
 class FalseyBoolException(Exception):
     def __bool__(self):
         return False

From b4de44eb62d42f4a4f6c09d61e8ea6691b4df281 Mon Sep 17 00:00:00 2001
From: "Yves.Duprat" <yduprat@gmail.com>
Date: Tue, 8 Apr 2025 14:53:28 +0200
Subject: [PATCH 9/9] Update last reviews and comments

---
 Lib/test/test_concurrent_futures/executor.py | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/Lib/test/test_concurrent_futures/executor.py b/Lib/test/test_concurrent_futures/executor.py
index 36f9d26287514b..95bf8fcd25bf54 100644
--- a/Lib/test/test_concurrent_futures/executor.py
+++ b/Lib/test/test_concurrent_futures/executor.py
@@ -23,6 +23,7 @@ def my_method(self):
 def make_dummy_object(_):
     return MyObject()
 
+
 # Used in test_swallows_falsey_exceptions
 def raiser(exception, msg='std'):
     raise exception(msg)
@@ -223,12 +224,12 @@ def test_free_reference(self):
     def test_swallows_falsey_exceptions(self):
         # see gh-132063: Prevent exceptions that evaluate as falsey
         # from being ignored.
-        # Falsey exceptions return 0 when `__len__` method is called,
-        # False when `__bool__` method is called.
+        # Recall: `x` is falsey if `len(x)` returns 0 or `bool(x)` returns False.
 
-        msg = 'lenlen'
-        with self.assertRaisesRegex(FalseyLenException, msg):
-            self.executor.submit(raiser, FalseyLenException, msg).result()
         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()