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

Cannot call dict.get with a falsy second argument and an 'or' expression. #5519

Open
ollien opened this issue Aug 22, 2018 · 8 comments
Open
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-1-normal

Comments

@ollien
Copy link

ollien commented Aug 22, 2018

In mypy 0.610, as well as the latest version from master, the following throws an error under Python 3.6

def foo(a: str):
    pass


d = {'key': 'a'}
foo(d.get('key', None) or '26')

test.py:6: error: Argument 2 to "get" of "Mapping" has incompatible type "None"; expected "str"

However, no matter what, foo will still receive a str, given that None is falsy. Therefore, no error should be thrown. This occurs with just a simple call to mypy foo.py with no flags.

@ollien ollien changed the title Cannot call dict.get with a falsy second argument and an 'or' expression. Cannot call dict.get with a falsy first argument and an 'or' expression. Aug 22, 2018
@ollien ollien changed the title Cannot call dict.get with a falsy first argument and an 'or' expression. Cannot call dict.get with a falsy second argument and an 'or' expression. Aug 22, 2018
@ilevkivskyi
Copy link
Member

Note that plain (i.e. without type context) or type-checks just fine, moreover it gets correctly inferred as str:

reveal_type(d.get('key', None) or '26')  # No error, revealed type is 'builtins.str'

So I can guess this is a weird variation of #4872 which is already high priority.

@ilevkivskyi ilevkivskyi added bug mypy got something wrong priority-1-normal false-positive mypy gave an error on correct code labels Aug 23, 2018
@jhbuhrman
Copy link

Should this really be viewed as an error? It depends on whether you would like to expect that dict.get should always return the type of the value (_V). Simple work-around in this case - perhaps also less error-prone (because this forces you to think about what to do if the dict actually would contain ''-values):

foo(d.get('key', '') or '26')

@ollien
Copy link
Author

ollien commented Sep 5, 2018

That shouldn't matter, though. Regardless of the fact that you can work around it, it is still impossible to have mismatched types in this code, making the error incorrect.

@jhbuhrman
Copy link

Yep, you're right. I was assuming that the default argument should be of the same type as the value type of the dict (_VT) so that dict.get(...) could promise to always return something of _VT (regardless if it would be called with one or two args), but this is not necessary in any way, of course. Apologies for the disturbance.

@ilevkivskyi
Copy link
Member

It turns out this is unrelated to #4872. The fix is simple but it involves compromises, so we need to wait a bit more to see how often this error appears.

@ollien
Copy link
Author

ollien commented Oct 1, 2018

@ilevkivskyi would you mind explaining what the compromises are?

@ilevkivskyi
Copy link
Member

One way to solve: provide an optional context for inference -- downside: it looks too ad-hoc.
Another way to solve: don't provide the context for l.h.s at all since it is often not needed -- downside: often != always.

@shizmob
Copy link

shizmob commented Dec 16, 2018

I seem to be getting a possibly-related error where mypy fails to eliminate the None case in an x or y context:

from typing import Dict, List, Optional, Iterable

class C:
    def __init__(self) -> None:
        self.sources: Dict[str, List[str]] = {}

    def process(self, name: str, sources: Optional[Iterable[str]] = None) -> None:
        reveal_type(sources)
        reveal_type(self.sources.get(name, []))
        reveal_type(sources or self.sources.get(name, []))
        to_process: Iterable[str] = sources or self.sources.get(name, [])
        print(to_process)

Running mypy --strict yields:

y.py:8: error: Revealed type is 'Union[typing.Iterable[builtins.str], None]'
y.py:9: error: Revealed type is 'builtins.list[builtins.str]'
y.py:10: error: Revealed type is 'Union[typing.Iterable[builtins.str], None]'
y.py:11: error: Incompatible types in assignment (expression has type "Optional[Iterable[str]]", variable has type "Iterable[str]")

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
Projects
None yet
Development

No branches or pull requests

4 participants