Skip to content

Changing order of isinstance checks breaks inferred type #4422

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

Closed
pirate opened this issue Jan 3, 2018 · 5 comments
Closed

Changing order of isinstance checks breaks inferred type #4422

pirate opened this issue Jan 3, 2018 · 5 comments
Labels
bug mypy got something wrong priority-1-normal

Comments

@pirate
Copy link

pirate commented Jan 3, 2018

This works fine:

def __add__(self, other: object) -> 'Currency':
    if isinstance(other, self.__class__) and isinstance(other, Currency):
        return self.__class__(other.amt + self.amt)
    return NotImplemented

but this does not:

def __add__(self, other: object) -> 'Currency':
    if isinstance(other, Currency) and isinstance(other, self.__class__):   # only expression order is flipped
        return self.__class__(other.amt + self.amt)
    return NotImplemented

throws: error: "object" has no attribute "amt"

Full code to reproduce it locally: https://gist.github.com/pirate/b3ef5a25449285dadc366dd926cebcea

The intention is to check that other is the same class as self (to prevent adding BTC to USD by accident). That check alone should imply that other is a subclass of Currency (since the __add__ method is defined on Currency), but it looks like I have to explicitly make both assertions, and make the Currency assertion after the self.__class__ one in order for mypy to narrow the type correctly.

Correct me if I'm mistaken, but I think there may be two bugs here:

  • The order of isinstance checks shouldn't matter (especially not in an and expression)
  • The isinstance(other, Currency) should not even be necessary, as it's implied that other is a Currency if other.__class__ == self.__class__

This issue discusses a vaguely similar type-narrowing problem, but I'm not sure if it's actually related: #2776

@pirate pirate changed the title Order of isinstance checks in an if statement changes the inferred type Order of isinstance checks in if statement changes the inferred type Jan 3, 2018
@pirate pirate changed the title Order of isinstance checks in if statement changes the inferred type Order of isinstance checks in if statement changes inferred type Jan 3, 2018
@ilevkivskyi
Copy link
Member

It is indeed close to #2776 And is also another hint that we need (at least internal) intersection type.

@ilevkivskyi ilevkivskyi added bug mypy got something wrong priority-1-normal labels Jan 3, 2018
@pirate pirate changed the title Order of isinstance checks in if statement changes inferred type Changing order of isinstance checks breaks inferred type Jan 3, 2018
@gvanrossum
Copy link
Member

Hm, the direct cause seems to me that mypy doesn't understand enough about self.__class__ -- it just knows it is a class, it doesn't know it is Type[Currency]. So isinstance(other, self.__class__) ends up being equivalent to isinstance(other, object). Plus, there's something wrong with the interpretation of and, seemingly it throws away the left operand in cases like this.

@ilevkivskyi
Copy link
Member

Hm, the direct cause seems to me that mypy doesn't understand enough about self.__class__ -- it just knows it is a class, it doesn't know it is Type[Currency].

Maybe this can be fixed by reapplying python/typeshed#1549? IIRC the issue caused by this reverted typeshed PR was solved some time ago. (Although this will not help in more complex situations with multiple inheritance like in #2776, that needs at least synthetic TypeInfos as proposed in #2776 (comment).)

@pirate
Copy link
Author

pirate commented Feb 4, 2020

Possibly fixed by #8305?

@97littleleaf11
Copy link
Collaborator

@pirate Thanks for pointing out! All three cases wouldn't generate errors now!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong priority-1-normal
Projects
None yet
Development

No branches or pull requests

4 participants