]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-39350: Fix fractions for int subclasses (GH-18375)
authorVictor Stinner <vstinner@python.org>
Fri, 7 Feb 2020 22:42:51 +0000 (23:42 +0100)
committerGitHub <noreply@github.com>
Fri, 7 Feb 2020 22:42:51 +0000 (23:42 +0100)
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.

Doc/library/fractions.rst
Lib/fractions.py
Lib/test/test_fractions.py
Misc/NEWS.d/next/Library/2020-02-06-13-34-52.bpo-39350.wRwup1.rst [new file with mode: 0644]

index d3a42762e3ff8da64bc23c756e306901aa405f43..a4d006eb58ffeb487aba0d1688fa6911dceb4f74 100644 (file)
@@ -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
 
index f5a854414c16699dc63e6a209a14148a724cb92b..de3e23b759227c3bc03cc299f272b12c513399cc 100644 (file)
@@ -155,13 +155,9 @@ class Fraction(numbers.Rational):
         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
index 4649a34bcc1f1a6e877730d13dcba19e1ef03f2d..c748533c7912981f5e997690de49ac37d7327149 100644 (file)
@@ -703,6 +703,28 @@ class FractionTest(unittest.TestCase):
         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 (file)
index 0000000..1a09358
--- /dev/null
@@ -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*.