Skip to content

Commit 705d271

Browse files
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 427c84f) Co-authored-by: Sebastian Berg <[email protected]>
1 parent 6ba8dc6 commit 705d271

File tree

3 files changed

+41
-1
lines changed

3 files changed

+41
-1
lines changed

Lib/fractions.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,9 @@ def __ge__(a, b):
625625

626626
def __bool__(a):
627627
"""a != 0"""
628-
return a._numerator != 0
628+
# bpo-39274: Use bool() because (a._numerator != 0) can return an
629+
# object which is not a bool.
630+
return bool(a._numerator)
629631

630632
# support for pickling, copy, and deepcopy
631633

Lib/test/test_fractions.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import numbers
77
import operator
88
import fractions
9+
import functools
910
import sys
1011
import unittest
1112
import warnings
@@ -335,6 +336,42 @@ def testConversions(self):
335336

336337
self.assertTypedEquals(0.1+0j, complex(F(1,10)))
337338

339+
def testBoolGuarateesBoolReturn(self):
340+
# Ensure that __bool__ is used on numerator which guarantees a bool
341+
# return. See also bpo-39274.
342+
@functools.total_ordering
343+
class CustomValue:
344+
denominator = 1
345+
346+
def __init__(self, value):
347+
self.value = value
348+
349+
def __bool__(self):
350+
return bool(self.value)
351+
352+
@property
353+
def numerator(self):
354+
# required to preserve `self` during instantiation
355+
return self
356+
357+
def __eq__(self, other):
358+
raise AssertionError("Avoid comparisons in Fraction.__bool__")
359+
360+
__lt__ = __eq__
361+
362+
# We did not implement all abstract methods, so register:
363+
numbers.Rational.register(CustomValue)
364+
365+
numerator = CustomValue(1)
366+
r = F(numerator)
367+
# ensure the numerator was not lost during instantiation:
368+
self.assertIs(r.numerator, numerator)
369+
self.assertIs(bool(r), True)
370+
371+
numerator = CustomValue(0)
372+
r = F(numerator)
373+
self.assertIs(bool(r), False)
374+
338375
def testRound(self):
339376
self.assertTypedEquals(F(-200), round(F(-150), -2))
340377
self.assertTypedEquals(F(-200), round(F(-250), -2))
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
``bool(fraction.Fraction)`` now returns a boolean even if (numerator != 0) does not return a boolean (ex: numpy number).

0 commit comments

Comments
 (0)