-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
bpo-39274: Ensure Fraction.__bool__
returns a bool
#18017
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
import numbers | ||
import operator | ||
import fractions | ||
import functools | ||
import sys | ||
import unittest | ||
import warnings | ||
|
@@ -346,6 +347,42 @@ def testConversions(self): | |
|
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The bug is that "numerator != 0" returns an object which is not a subtype of bool. Does your test reproduce the bug if you revert the fractions.py change? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, I am testing that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, tried to make it a bit more waterproof (by resetting Its over engineered, but right now I do not have a better idea TBH (unless we just skip the test...) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You cannot use 3rd party modules like numpy in the Python test suite, sadly. (At least, we are trying hard to avoid that.) So you should try to mimick numpy's bool behavior. Please just implement a ne() method which raises an AssertionError. It would make me more comfortable. Or better: implement eq and lt, and @ @functools.total_ordering: Again, these methods would raise an exception. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Next try... Had tried to avoid registering with Overall, seems much better, since now it really is a crippled object that cannot do anything but what it is supposed to do... |
||
|
||
@property | ||
def numerator(self): | ||
# required to preserve `self` during instantiation | ||
return self | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer to remove that and use F(numerator, 1) instead. |
||
|
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may fail if you run the test with reference leak test: ./python -m test -R 3:3 test_fractions Can you check that it does not leak? I would prefer to avoid .register() if it does leak. ... This test is getting more and more complex... Maybe we should just ignore the test and just merge the fix. What do you think @mdickinson? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm, seems to work:
I have not used the python refcount checker, so not sure if it clears ABCs are not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fix itself looks good to me; I think we should merge as is. |
||
|
||
numerator = CustomValue(1) | ||
r = F(numerator) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer to explicit the denominator: F(numerator, 1). Same below. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That does not change much, if I add that, the only difference is that I also have to define |
||
# 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)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
``bool(fraction.Fraction)`` now returns a boolean even if (numerator != 0) does not return a boolean (ex: numpy number). |
Uh oh!
There was an error while loading. Please reload this page.