summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/fractions.rst4
-rw-r--r--Doc/whatsnew/3.11.rst8
-rw-r--r--Lib/fractions.py9
-rw-r--r--Lib/test/test_fractions.py42
-rw-r--r--Misc/NEWS.d/next/Library/2021-08-20-10-52-40.bpo-44547.eu0iJq.rst2
5 files changed, 62 insertions, 3 deletions
diff --git a/Doc/library/fractions.rst b/Doc/library/fractions.rst
index d04de8f..c893f2d 100644
--- a/Doc/library/fractions.rst
+++ b/Doc/library/fractions.rst
@@ -94,6 +94,10 @@ another rational number, or from a string.
Underscores are now permitted when creating a :class:`Fraction` instance
from a string, following :PEP:`515` rules.
+ .. versionchanged:: 3.11
+ :class:`Fraction` implements ``__int__`` now to satisfy
+ ``typing.SupportsInt`` instance checks.
+
.. attribute:: numerator
Numerator of the Fraction in lowest term.
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index 74fc753..a03fff8 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -193,8 +193,12 @@ Improved Modules
fractions
---------
-Support :PEP:`515`-style initialization of :class:`~fractions.Fraction` from
-string. (Contributed by Sergey B Kirpichev in :issue:`44258`.)
+* Support :PEP:`515`-style initialization of :class:`~fractions.Fraction` from
+ string. (Contributed by Sergey B Kirpichev in :issue:`44258`.)
+
+* :class:`~fractions.Fraction` now implements an ``__int__`` method, so
+ that an ``isinstance(some_fraction, typing.SupportsInt)`` check passes.
+ (Contributed by Mark Dickinson in :issue:`44547`.)
math
diff --git a/Lib/fractions.py b/Lib/fractions.py
index 180cd94..f9ac882 100644
--- a/Lib/fractions.py
+++ b/Lib/fractions.py
@@ -594,8 +594,15 @@ class Fraction(numbers.Rational):
"""abs(a)"""
return Fraction(abs(a._numerator), a._denominator, _normalize=False)
+ def __int__(a, _index=operator.index):
+ """int(a)"""
+ if a._numerator < 0:
+ return _index(-(-a._numerator // a._denominator))
+ else:
+ return _index(a._numerator // a._denominator)
+
def __trunc__(a):
- """trunc(a)"""
+ """math.trunc(a)"""
if a._numerator < 0:
return -(-a._numerator // a._denominator)
else:
diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py
index bbf7709..fc46e86 100644
--- a/Lib/test/test_fractions.py
+++ b/Lib/test/test_fractions.py
@@ -8,6 +8,7 @@ import operator
import fractions
import functools
import sys
+import typing
import unittest
from copy import copy, deepcopy
import pickle
@@ -385,6 +386,47 @@ class FractionTest(unittest.TestCase):
self.assertTypedEquals(0.1+0j, complex(F(1,10)))
+ def testSupportsInt(self):
+ # See bpo-44547.
+ f = F(3, 2)
+ self.assertIsInstance(f, typing.SupportsInt)
+ self.assertEqual(int(f), 1)
+ self.assertEqual(type(int(f)), int)
+
+ def testIntGuaranteesIntReturn(self):
+ # Check that int(some_fraction) gives a result of exact type `int`
+ # even if the fraction is using some other Integral type for its
+ # numerator and denominator.
+
+ class CustomInt(int):
+ """
+ Subclass of int with just enough machinery to convince the Fraction
+ constructor to produce something with CustomInt numerator and
+ denominator.
+ """
+
+ @property
+ def numerator(self):
+ return self
+
+ @property
+ def denominator(self):
+ return CustomInt(1)
+
+ def __mul__(self, other):
+ return CustomInt(int(self) * int(other))
+
+ def __floordiv__(self, other):
+ return CustomInt(int(self) // int(other))
+
+ f = F(CustomInt(13), CustomInt(5))
+
+ self.assertIsInstance(f.numerator, CustomInt)
+ self.assertIsInstance(f.denominator, CustomInt)
+ self.assertIsInstance(f, typing.SupportsInt)
+ self.assertEqual(int(f), 2)
+ self.assertEqual(type(int(f)), int)
+
def testBoolGuarateesBoolReturn(self):
# Ensure that __bool__ is used on numerator which guarantees a bool
# return. See also bpo-39274.
diff --git a/Misc/NEWS.d/next/Library/2021-08-20-10-52-40.bpo-44547.eu0iJq.rst b/Misc/NEWS.d/next/Library/2021-08-20-10-52-40.bpo-44547.eu0iJq.rst
new file mode 100644
index 0000000..a5f425e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-08-20-10-52-40.bpo-44547.eu0iJq.rst
@@ -0,0 +1,2 @@
+Implement ``Fraction.__int__``, so that a :class:`fractions.Fraction`
+instance ``f`` passes an ``isinstance(f, typing.SupportsInt)`` check.