This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: __floordiv__ in module fraction fails with TypeError instead of returning NotImplemented
Type: behavior Stage: patch review
Components: Library (Lib) Versions: Python 3.6, Python 3.2, Python 3.3, Python 3.4, Python 3.5, Python 2.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: ShashkovS
Priority: normal Keywords: patch

Created on 2015-10-15 14:09 by ShashkovS, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
fractions_truediv_fix.patch ShashkovS, 2015-10-16 20:59 fractions module fix in floordiv and mod review
Pull Requests
URL Status Linked Edit
PR 11322 ShashkovS, 2019-01-11 10:31
Repositories containing patches
https://hg.python.org/cpython
Messages (8)
msg253046 - (view) Author: Sergey Shashkov (ShashkovS) * Date: 2015-10-15 14:09
__floordiv__ in module fraction fails with TypeError instead of returning NotImplemented when modulo is a class without __rtruediv__ or __mul__.

Code sample:

class Foo(object):
    def __rdivmod__(self, other):
        return 'rdivmod works'

from fractions import Fraction
a = Fraction(1,1)
b = Foo()
print(divmod(1, b))
print(divmod(a, b))



__divmod__ in Fraction is inherited from class Real (numbers.py):
    def __divmod__(self, other):
        return (self // other, self % other)

So __floordiv__ and __mod__ are called. 

    def __floordiv__(a, b):
        """a // b"""
        return math.floor(a / b)

    def __mod__(a, b):
        """a % b"""
        div = a // b
        return a - b * div

__floordiv__ if fractions.py makes a true division, and __mod__ makes multiplication.



The following code will fix the problem:


    def __divmod__(self, other):
        if isinstance(a, numbers.Complex):
            return (self // other, self % other)
        else:
            return NotImplemented
msg253047 - (view) Author: Sergey Shashkov (ShashkovS) * Date: 2015-10-15 14:12
def __floordiv__(a, b):
        """a // b"""
        if isinstance(b, numbers.Complex):
            return math.floor(a / b)
        else:
            return NotImplemented

And the same for __mod__.
msg253052 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2015-10-15 15:27
Can you please write a patch? See https://docs.python.org/devguide/
msg253077 - (view) Author: Oscar Benjamin (oscarbenjamin) * Date: 2015-10-16 17:16
You should test the change with number types that don't use the number tower e.g. Decimal, sympy, gmpy2, mpf, numpy arrays etc. Few non stdlib types use the number ABCs so testing against numbers.Complex may cause a change in behaviour.
msg253081 - (view) Author: Sergey Shashkov (ShashkovS) * Date: 2015-10-16 18:14
OK,

then we should not change numbers.py.

And in fractions.py:

def __floordiv__(a, b):
        """a // b"""
        if isinstance(b, numbers.Complex) or hasattr(b, '__rtruediv__'):
            fr = a / b
            if fr != NotImplemented:
                return math.floor(a / b)
            else:
                return NotImplemented
        else:
            return NotImplemented
msg253083 - (view) Author: Sergey Shashkov (ShashkovS) * Date: 2015-10-16 18:18
Bad idea, just


def __floordiv__(a, b):
        """a // b"""
        if isinstance(b, numbers.Complex):
            return math.floor(a / b)
        else:
            return NotImplemented

If b is inherited from number, real, complex, Fraction and etc, then a of type Fraction knows, how do make a division.
Otherwise may be b has __rfloordiv__, that khows how to be divided by Fraction.
msg253096 - (view) Author: Sergey Shashkov (ShashkovS) * Date: 2015-10-16 20:44
...
        def forward(a, b):
            if isinstance(b, (int, Fraction)):
                return monomorphic_operator(a, b)
            elif isinstance(b, float):
                return fallback_operator(float(a), b)
            elif isinstance(b, complex):
                return fallback_operator(complex(a), b)
            else:
                return NotImplemented
        forward.__name__ = '__' + fallback_operator.__name__ + '__'
        forward.__doc__ = monomorphic_operator.__doc__

        def reverse(b, a):
            if isinstance(a, numbers.Rational):
                # Includes ints.
                return monomorphic_operator(a, b)
            elif isinstance(a, numbers.Real):
                return fallback_operator(float(a), float(b))
            elif isinstance(a, numbers.Complex):
                return fallback_operator(complex(a), complex(b))
            else:
                return NotImplemented
...
so division is possible only with int, Fraction, float, complex, numbers.Rational, numbers.Real, numbers.Complex.
For all of them "isinstance(b, numbers.Complex)" is true
msg333448 - (view) Author: Sergey Shashkov (ShashkovS) * Date: 2019-01-11 10:31
This patch actually fixes the problem:

https://bugs.python.org/issue35588
https://github.com/python/cpython/commit/3a374e0c5abe805667b71ffaaa7614781101ff4c




from fractions import Fraction
import operator

class Goo:
    __radd__, __rdivmod__, __rfloordiv__, __rmod__, __rmul__, __rpow__, __rsub__, __rtruediv__ = [lambda a, b: 'ok'] * 8

for func in operator.add, operator.sub, operator.mul, operator.truediv, operator.pow, operator.mod, operator.floordiv, divmod:
    print(func.__name__, func(Fraction(1), Goo()))
History
Date User Action Args
2022-04-11 14:58:22adminsetgithub: 69598
2019-01-11 10:31:46ShashkovSsetstatus: open -> closed


nosy: - mark.dickinson, vstinner, oscarbenjamin
stage: patch review
messages: + msg333448
resolution: fixed
pull_requests: + pull_request11085
2015-10-16 20:59:31ShashkovSsetfiles: + fractions_truediv_fix.patch
hgrepos: + hgrepo321
keywords: + patch
2015-10-16 20:49:58ShashkovSsethgrepos: - hgrepo320
2015-10-16 20:48:13ShashkovSsethgrepos: + hgrepo320
2015-10-16 20:44:21ShashkovSsetmessages: + msg253096
2015-10-16 18:18:34ShashkovSsetmessages: + msg253083
2015-10-16 18:14:07ShashkovSsetmessages: + msg253081
2015-10-16 17:16:27oscarbenjaminsetnosy: + oscarbenjamin
messages: + msg253077
2015-10-15 15:27:15vstinnersetnosy: + vstinner
messages: + msg253052
2015-10-15 14:22:52r.david.murraysetnosy: + mark.dickinson
2015-10-15 14:12:59ShashkovSsetmessages: + msg253047
2015-10-15 14:09:54ShashkovScreate