diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2020-02-06 07:13:38 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-06 07:13:38 -0800 |
commit | 0d03a1028200646479ef9bb0ad8973d0e73f9525 (patch) | |
tree | 98bdca24fac5745d2f0802d37499d35769cc9005 | |
parent | 708f472dd92f4f46c27ace710492da65da4a3319 (diff) | |
download | cpython-git-0d03a1028200646479ef9bb0ad8973d0e73f9525.tar.gz |
bpo-39274: Ensure Fraction.__bool__() returns a bool (GH-18017)
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>
-rw-r--r-- | Lib/fractions.py | 4 | ||||
-rw-r--r-- | Lib/test/test_fractions.py | 37 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2020-01-15-23-13-03.bpo-39274.lpc0-n.rst | 1 |
3 files changed, 41 insertions, 1 deletions
diff --git a/Lib/fractions.py b/Lib/fractions.py index e774d58e40..e4fcc8901b 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -636,7 +636,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 diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py index 18ab28cfeb..86b49f30f5 100644 --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -6,6 +6,7 @@ import math import numbers import operator import fractions +import functools import sys import unittest import warnings @@ -346,6 +347,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 index 0000000000..4c398682b9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-01-15-23-13-03.bpo-39274.lpc0-n.rst @@ -0,0 +1 @@ +``bool(fraction.Fraction)`` now returns a boolean even if (numerator != 0) does not return a boolean (ex: numpy number). |