From b920b5d56c0b260aa5655337f21d857483861d5b Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 21 Mar 2022 09:57:44 +0300 Subject: [PATCH] bpo-47079: return a correct type from the Integral.denominator Also this adds tests for default methods of the Integral class. --- Lib/numbers.py | 2 +- Lib/test/test_abstract_numbers.py | 63 +++++++++++++++++++ .../2022-03-21-07-33-45.bpo-47079.CmpCm3.rst | 1 + 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2022-03-21-07-33-45.bpo-47079.CmpCm3.rst diff --git a/Lib/numbers.py b/Lib/numbers.py index 5b98e642083b36..3a7056fbbe81bc 100644 --- a/Lib/numbers.py +++ b/Lib/numbers.py @@ -388,6 +388,6 @@ def numerator(self): @property def denominator(self): """Integers have a denominator of 1.""" - return 1 + return type(self)(1) Integral.register(int) diff --git a/Lib/test/test_abstract_numbers.py b/Lib/test/test_abstract_numbers.py index 2e06f0d16fdd05..cf4dfab2dd9612 100644 --- a/Lib/test/test_abstract_numbers.py +++ b/Lib/test/test_abstract_numbers.py @@ -5,6 +5,61 @@ import unittest from numbers import Complex, Real, Rational, Integral + +class DummyIntegral(Integral): + """Dummy Integral class to test default implementations of methods.""" + + def __init__(self, val=0): + self._val = int(val) + + def __int__(self): + return self._val + + def __pow__(self, exponent, modulus=None): + return NotImplemented + __rpow__ = __pow__ + + def __lshift__(self, other): + return NotImplemented + __rlshift__ = __lshift__ + __rshift__ = __lshift__ + __rrshift__ = __lshift__ + __and__ = __lshift__ + __rand__ = __lshift__ + __xor__ = __lshift__ + __rxor__ = __lshift__ + __or__ = __lshift__ + __ror__ = __lshift__ + __add__ = __lshift__ + __radd__ = __lshift__ + __mul__ = __lshift__ + __rmul__ = __lshift__ + __mod__ = __lshift__ + __rmod__ = __lshift__ + __floordiv__ = __lshift__ + __rfloordiv__ = __lshift__ + __truediv__ = __lshift__ + __rtruediv__ = __lshift__ + __lt__ = __lshift__ + __le__ = __lshift__ + + def __eq__(self, other): + return all(isinstance(_, DummyIntegral) + for _ in [self, other]) and self._val == other._val + + def __invert__(self): + return NotImplemented + __abs__ = __invert__ + __neg__ = __invert__ + __trunc__ = __invert__ + __ceil__ = __invert__ + __floor__ = __invert__ + __round__ = __invert__ + + def __pos__(self): + return self + + class TestNumbers(unittest.TestCase): def test_int(self): self.assertTrue(issubclass(int, Integral)) @@ -17,6 +72,14 @@ def test_int(self): self.assertEqual(7, int(7).numerator) self.assertEqual(1, int(7).denominator) + def test_DummyIntegral(self): + seven = DummyIntegral(7) + one = DummyIntegral(1) + self.assertEqual(float(seven), 7.0) + self.assertEqual(float(one), 1.0) + self.assertEqual(seven.numerator, seven) + self.assertEqual(seven.denominator, one) + def test_float(self): self.assertFalse(issubclass(float, Rational)) self.assertTrue(issubclass(float, Real)) diff --git a/Misc/NEWS.d/next/Library/2022-03-21-07-33-45.bpo-47079.CmpCm3.rst b/Misc/NEWS.d/next/Library/2022-03-21-07-33-45.bpo-47079.CmpCm3.rst new file mode 100644 index 00000000000000..a73c04be00fc2e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-03-21-07-33-45.bpo-47079.CmpCm3.rst @@ -0,0 +1 @@ +Correct the implementation of :attr:`Integral.denominator`.