Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistency with unions and overloads in when checking overlapping op methods #2129

Closed
Michael0x2a opened this issue Sep 14, 2016 · 2 comments
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-1-normal topic-overloads topic-union-types

Comments

@Michael0x2a
Copy link
Collaborator

As a part of my adventures in attempting to add semi-usable stubs for the fractions and statistics modules, I ran into an edge case involving the __add__ and __radd__ methods (or any pair of operator methods). Here is a simplified version of decimal.Decimal:

from typing import Union

class FakeDecimal1:
    def __add__(self, other: Union[int, FakeDecimal1]) -> FakeDecimal1:
        return FakeDecimal1()
    def __radd__(self, other: Union[int, FakeDecimal1]) -> FakeDecimal1:
        return FakeDecimal1()

When typechecking this, I got the following (and somewhat cryptic) error message:

experiments\test.py: note: In member "__radd__" of class "FakeDecimal":
experiments\test.py:6: error: Forward operator "__add__" is not callable

It seems mypy wanted me to do this instead:

from typing import Union

class FakeDecimal2:
    def __add__(self, other: Union[int, FakeDecimal2]) -> FakeDecimal2:
        return FakeDecimal2()
    def __radd__(self, other: int) -> FakeDecimal2:
        return FakeDecimal2()

However, the following code which uses overrides instead of Union does not result in any errors, which feels inconsistent:

class FakeDecimal3:
    @overload
    def __add__(self, other: int) -> FakeDecimal3:
        return FakeDecimal2()
    @overload
    def __add__(self, other: FakeDecimal3) -> FakeDecimal3:
        return FakeDecimal3()
    @overload
    def __radd__(self, other: int) -> FakeDecimal3:
        return FakeDecimal2()
    @overload
    def __radd__(self, other: FakeDecimal3) -> FakeDecimal3:
        return FakeDecimal2()

After some digging, it appeared to me that the reason for this behavior is because the check_overlapping_op_methods within checker.py handles the case where forward_type is either a CallableType, Overloaded, or AnyType, but not the case where it's a UnionType.

Is this intentional, or an oversight? I think this is a bug, mainly since if mypy accepts version 3, it ought to accept verison 1? Or perhaps the fact that __radd__ should never need to accept a value of the same type as its instance means that the version 2 is more correct and versions 1 and 3 ought to be rejected?

(For what it's worth, adding a new case to make UnionType do the same thing as the Overloaded case makes version 1 pass.)

This is perhaps related: #1442

@gvanrossum
Copy link
Member

There's also #1264, and and perhaps #1987. Jukka has said a few times that
the checks are too strict and should be fixed. So... eat your heart out!

On Wed, Sep 14, 2016 at 12:19 AM, Michael Lee [email protected]
wrote:

As a part of my adventures in attempting to add semi-usable stubs for the
fractions and statistics modules, I ran into an edge case involving the
add and radd methods (or any pair of operator methods). Here is a
simplified version of decimal.Decimal:

from typing import Union
class FakeDecimal1:
def add(self, other: Union[int, FakeDecimal1]) -> FakeDecimal1:
return FakeDecimal1()
def radd(self, other: Union[int, FakeDecimal1]) -> FakeDecimal1:
return FakeDecimal1()

When typechecking this, I got the following (and somewhat cryptic) error
message:

experiments\test.py: note: In member "radd" of class "FakeDecimal":
experiments\test.py:6: error: Forward operator "add" is not callable

It seems mypy wanted me to do this instead:

from typing import Union
class FakeDecimal2:
def add(self, other: Union[int, FakeDecimal2]) -> FakeDecimal2:
return FakeDecimal2()
def radd(self, other: int) -> FakeDecimal2:
return FakeDecimal2()

However, the following code which uses overrides instead of Union does
not result in any errors, which feels inconsistent:

class FakeDecimal3:
@overload
def add(self, other: int) -> FakeDecimal3:
return FakeDecimal2()
@overload
def add(self, other: FakeDecimal3) -> FakeDecimal3:
return FakeDecimal3()
@overload
def radd(self, other: int) -> FakeDecimal3:
return FakeDecimal2()
@overload
def radd(self, other: FakeDecimal3) -> FakeDecimal3:
return FakeDecimal2()

After some digging, it appeared to me that the reason for this behavior is
because the check_overlapping_op_methods

def check_overlapping_op_methods(self,

within checker.py handles the case where forward_type is either a
CallableType, Overloaded, or AnyType, but not the case where it's a
UnionType.

Is this intentional, or an oversight? I think this is a bug, mainly
since if mypy accepts version 3, it ought to accept verison 1? Or perhaps
the fact that radd should never need to accept a value of the same
type as its instance means that the version 2 is more correct and versions
1 and 3 ought to be rejected?

(For what it's worth, adding a new case to make UnionType do the same
thing as the Overloaded case makes version 1 pass.)

This is perhaps related: #1442
#1442


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#2129, or mute the thread
https://github.com/notifications/unsubscribe-auth/ACwrMssgEMm2l0edVgW7YOrdC83uC-7Cks5qp6ACgaJpZM4J8eCw
.

--Guido van Rossum (python.org/~guido)

mkurek pushed a commit to mkurek/typeshed that referenced this issue Dec 21, 2016
Improve operator methods for dateutil.relativedelta stubs:
* `__add__` operator method could return other types than `relativedelta` (`datetime.date` or `datetime.datetime`)
* use specific types of operators args instead of Any
* mypy currently does not handle `Union` in op methods (see python/mypy#2129, python/mypy#1442, python/mypy#1264 for details), so I've overloaded it directly
ambv pushed a commit to python/typeshed that referenced this issue Dec 28, 2016
Improve operator methods for dateutil.relativedelta stubs:
* `__add__` operator method could return other types than `relativedelta` (`datetime.date` or `datetime.datetime`)
* use specific types of operators args instead of Any
* mypy currently does not handle `Union` in op methods (see python/mypy#2129, python/mypy#1442, python/mypy#1264 for details), so I've overloaded it directly
@JukkaL JukkaL added the false-positive mypy gave an error on correct code label May 18, 2018
@Michael0x2a
Copy link
Collaborator Author

I tried re-running all three of the above examples on master, and they all seem to typecheck now... I don't think it was due to any of my overload changes though -- I suspect this was fixed a year ago by #3086?

(Also, apparently I was the one who originally submitted this issue? I completely forgot about this...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-1-normal topic-overloads topic-union-types
Projects
None yet
Development

No branches or pull requests

4 participants