[Python-checkins] bpo-39350: Fix fractions for int subclasses (GH-18375)
Victor Stinner
webhook-mailer at python.org
Fri Feb 7 17:42:56 EST 2020
https://github.com/python/cpython/commit/dc7a50d73a3d16918529669ff7b8783c08cff090
commit: dc7a50d73a3d16918529669ff7b8783c08cff090
branch: master
author: Victor Stinner <vstinner at python.org>
committer: GitHub <noreply at github.com>
date: 2020-02-07T23:42:51+01:00
summary:
bpo-39350: Fix fractions for int subclasses (GH-18375)
Fix regression in fractions.Fraction if the numerator and/or the
denominator is an int subclass. The math.gcd() function is now
used to normalize the numerator and denominator. math.gcd() always
return a int type. Previously, the GCD type depended on numerator
and denominator.
files:
A Misc/NEWS.d/next/Library/2020-02-06-13-34-52.bpo-39350.wRwup1.rst
M Doc/library/fractions.rst
M Lib/fractions.py
M Lib/test/test_fractions.py
diff --git a/Doc/library/fractions.rst b/Doc/library/fractions.rst
index d3a42762e3ff8..a4d006eb58ffe 100644
--- a/Doc/library/fractions.rst
+++ b/Doc/library/fractions.rst
@@ -84,6 +84,10 @@ another rational number, or from a string.
The :class:`Fraction` constructor now accepts :class:`float` and
:class:`decimal.Decimal` instances.
+ .. versionchanged:: 3.9
+ The :func:`math.gcd` function is now used to normalize the *numerator*
+ and *denominator*. :func:`math.gcd` always return a :class:`int` type.
+ Previously, the GCD type depended on *numerator* and *denominator*.
.. attribute:: numerator
diff --git a/Lib/fractions.py b/Lib/fractions.py
index f5a854414c166..de3e23b759227 100644
--- a/Lib/fractions.py
+++ b/Lib/fractions.py
@@ -155,13 +155,9 @@ def __new__(cls, numerator=0, denominator=None, *, _normalize=True):
if denominator == 0:
raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
if _normalize:
- if type(numerator) is int is type(denominator):
- # *very* normal case
- g = math.gcd(numerator, denominator)
- if denominator < 0:
- g = -g
- else:
- g = _gcd(numerator, denominator)
+ g = math.gcd(numerator, denominator)
+ if denominator < 0:
+ g = -g
numerator //= g
denominator //= g
self._numerator = numerator
diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py
index 4649a34bcc1f1..c748533c79129 100644
--- a/Lib/test/test_fractions.py
+++ b/Lib/test/test_fractions.py
@@ -703,6 +703,28 @@ def test_slots(self):
r = F(13, 7)
self.assertRaises(AttributeError, setattr, r, 'a', 10)
+ def test_int_subclass(self):
+ class myint(int):
+ def __mul__(self, other):
+ return type(self)(int(self) * int(other))
+ def __floordiv__(self, other):
+ return type(self)(int(self) // int(other))
+ def __mod__(self, other):
+ x = type(self)(int(self) % int(other))
+ return x
+ @property
+ def numerator(self):
+ return type(self)(int(self))
+ @property
+ def denominator(self):
+ return type(self)(1)
+
+ f = fractions.Fraction(myint(1 * 3), myint(2 * 3))
+ self.assertEqual(f.numerator, 1)
+ self.assertEqual(f.denominator, 2)
+ self.assertEqual(type(f.numerator), myint)
+ self.assertEqual(type(f.denominator), myint)
+
if __name__ == '__main__':
unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2020-02-06-13-34-52.bpo-39350.wRwup1.rst b/Misc/NEWS.d/next/Library/2020-02-06-13-34-52.bpo-39350.wRwup1.rst
new file mode 100644
index 0000000000000..1a09358082ef6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-02-06-13-34-52.bpo-39350.wRwup1.rst
@@ -0,0 +1,5 @@
+Fix regression in :class:`fractions.Fraction` if the numerator and/or the
+denominator is an :class:`int` subclass. The :func:`math.gcd` function is now
+used to normalize the *numerator* and *denominator*. :func:`math.gcd` always
+return a :class:`int` type. Previously, the GCD type depended on *numerator*
+and *denominator*.
More information about the Python-checkins
mailing list