Skip to content

mypy doesn't understand that Optional is no longer Optional in an 'if' clause #8000

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
1st1 opened this issue Nov 21, 2019 · 9 comments
Closed
Labels

Comments

@1st1
Copy link
Member

1st1 commented Nov 21, 2019

mypy doesn't understand that Optional is no longer Optional in an 'if' clause:

test.py:

from typing import *


class Item:
    pass


class Foo:
    def __init__(self, ignore: Optional[AbstractSet[Item]] = None) -> None:
        self.ignore = ignore

    def do(self, commands: Sequence[Item]) -> None:
        if self.ignore:
            commands = list(filter(lambda c: c in self.ignore, commands))
$ mypy --version
mypy 0.740

$ mypy --strict test.py
test.py:14: error: Unsupported right operand type for in ("Optional[AbstractSet[Item]]")
Found 1 error in 1 file (checked 1 source file)
@emmatyping
Copy link
Member

Hi Yury! You need to do if self.ignore is None for mypy to pick up that you want self.ignore to be non-None.

There has been previous discussion about allowing if var to narrow var, but I believe we decided to not go down that route as it could get complicated?

@msullivan
Copy link
Collaborator

msullivan commented Nov 22, 2019 via email

@1st1
Copy link
Member Author

1st1 commented Nov 22, 2019

Hi Yury! You need to do if self.ignore is None

Hi!

We do support narrowing with if var.

Yeah, changing to if is not None doesn't help.

a lambda, which could get called after the value is changed.

Not sure I follow. self.ignore isn't changed anywhere. It's non-None inside the if clause and the filter() function is called right away.

Upd: Ideally mypy should infer that the lambda is used synchronously in this case. Would it be possible to fix this? Or should our code be rewritten to close over a temp variable?

@ilevkivskyi
Copy link
Member

Duplicate of #4973 (which is itself a special case of #2608).

@emmatyping
Copy link
Member

We do support narrowing with if var

Oh huh, I must have missed that, apologies!

@ilevkivskyi
Copy link
Member

Would it be possible to fix this?

Definitelly possible, but it may be non-trivial and I am not sure we will have time for this, but PRs are very welcome.

Or should our code be rewritten to close over a temp variable?

Yep, a temp variable is a quick and simple workaround.

@1st1
Copy link
Member Author

1st1 commented Nov 22, 2019

Thank you guys for a quick turnaround!

@alan-isaac
Copy link

Alternatively, adding an assertion also seems to satisfy mypy. Is that reliable?

I'm finding this problem generally emerges when an instance variable must be initialized to None but will never be gotten until after a value is set. What is the preferred way to communicate this to mypy?

@hauntsaninja
Copy link
Collaborator

Yes, an assertion works well.

For the instance variable thing you can lie to mypy and just give it a non-None type and use type ignore. Then it's on you to make sure your init logic is right.

Alternatively, you could use a @Property that contains an assert and returns a non-Optional type

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants