]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-39274: Ensure Fraction.__bool__() returns a bool (GH-18017)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 6 Feb 2020 15:14:37 +0000 (07:14 -0800)
committerGitHub <noreply@github.com>
Thu, 6 Feb 2020 15:14:37 +0000 (07:14 -0800)
Some numerator types used (specifically NumPy) decides to not
return a Python boolean for the "a != b" operation. Using the equivalent
call to bool() guarantees a bool return also for such types.
(cherry picked from commit 427c84f13f7719e6014a21bd1b81efdc02a046fb)

Co-authored-by: Sebastian Berg <sebastian@sipsolutions.net>
Lib/fractions.py
Lib/test/test_fractions.py
Misc/NEWS.d/next/Library/2020-01-15-23-13-03.bpo-39274.lpc0-n.rst [new file with mode: 0644]

index 8330202d7037b3090f7e75dd4495fb1058712ed5..64bff70060eb8f03d5485eb65f97164befdc4c56 100644 (file)
@@ -625,7 +625,9 @@ class Fraction(numbers.Rational):
 
     def __bool__(a):
         """a != 0"""
-        return a._numerator != 0
+        # bpo-39274: Use bool() because (a._numerator != 0) can return an
+        # object which is not a bool.
+        return bool(a._numerator)
 
     # support for pickling, copy, and deepcopy
 
index 7905c367ba9dd24464c18809c87a146873ee5ba7..d1a43870ea88a50242910441c118c5f8bcb46c3b 100644 (file)
@@ -6,6 +6,7 @@ import math
 import numbers
 import operator
 import fractions
+import functools
 import sys
 import unittest
 import warnings
@@ -335,6 +336,42 @@ class FractionTest(unittest.TestCase):
 
         self.assertTypedEquals(0.1+0j, complex(F(1,10)))
 
+    def testBoolGuarateesBoolReturn(self):
+        # Ensure that __bool__ is used on numerator which guarantees a bool
+        # return.  See also bpo-39274.
+        @functools.total_ordering
+        class CustomValue:
+            denominator = 1
+
+            def __init__(self, value):
+                self.value = value
+
+            def __bool__(self):
+                return bool(self.value)
+
+            @property
+            def numerator(self):
+                # required to preserve `self` during instantiation
+                return self
+
+            def __eq__(self, other):
+                raise AssertionError("Avoid comparisons in Fraction.__bool__")
+
+            __lt__ = __eq__
+
+        # We did not implement all abstract methods, so register:
+        numbers.Rational.register(CustomValue)
+
+        numerator = CustomValue(1)
+        r = F(numerator)
+        # ensure the numerator was not lost during instantiation:
+        self.assertIs(r.numerator, numerator)
+        self.assertIs(bool(r), True)
+
+        numerator = CustomValue(0)
+        r = F(numerator)
+        self.assertIs(bool(r), False)
+
     def testRound(self):
         self.assertTypedEquals(F(-200), round(F(-150), -2))
         self.assertTypedEquals(F(-200), round(F(-250), -2))
diff --git a/Misc/NEWS.d/next/Library/2020-01-15-23-13-03.bpo-39274.lpc0-n.rst b/Misc/NEWS.d/next/Library/2020-01-15-23-13-03.bpo-39274.lpc0-n.rst
new file mode 100644 (file)
index 0000000..4c39868
--- /dev/null
@@ -0,0 +1 @@
+``bool(fraction.Fraction)`` now returns a boolean even if (numerator != 0) does not return a boolean (ex: numpy number).