Skip to content

Mypy infers x.get("a") or x.get("b", "c") to str | None, even when x is dict[str, str] #15534

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
hfcredidio opened this issue Jun 27, 2023 · 6 comments
Labels
bug mypy got something wrong

Comments

@hfcredidio
Copy link

Bug Report

Mypy wrongly infers the type of the expression x.get("a") or x.get("b", "c") to be str | None, even when x is explicitly a dict[str, str].

To Reproduce

def works(x: dict[str, str]) -> str:
    a = x.get("a")
    b = x.get("b", "c")
    return a or b

def also_works(x: dict[str, str]) -> str: return x.get("a", x.get("b", "c"))
def also_fine(x: dict[str, str]) -> str: return x.get("a") or x.get("b") or "c"
def breaks(x: dict[str, str]) -> str: return x.get("a") or x.get("b", "c")

https://mypy-play.net/?mypy=latest&python=3.11&gist=33335d3d1d19e5858beb76ed65ce8470

I realise the examples are not logically equivalent, but they should all evaluate to strings

Expected Behavior

No errors.

Actual Behavior

Mypy returns main.py:8: error: Incompatible return value type (got "str | None", expected "str") [return-value]

Your Environment

  • Mypy version used: 1.4.0
  • Mypy command-line flags: N/A
  • Mypy configuration options from mypy.ini (and other config files): N/A
  • Python version used: 3.11
@hfcredidio hfcredidio added the bug mypy got something wrong label Jun 27, 2023
@tmke8
Copy link
Contributor

tmke8 commented Jun 27, 2023

Very strange error. If I had to guess, I'd say it has to do with the slightly weird signature of .get(): python/typeshed#10294

You could try defining your own .get() as in the linked PR and see whether the problem persists.

@ikonst
Copy link
Contributor

ikonst commented Jun 27, 2023

@AlexWaygood topic-type-context?

@ikonst
Copy link
Contributor

ikonst commented Jun 27, 2023

Ahh, never mind :(

def f4(x: dict[str, str]) -> str:
    q = x.get("a") or x.get("b", "c")  # N: Revealed type is "Union[builtins.str, None]"
    reveal_type(q)
    return q  # E: Incompatible return value type (got "str | None", expected "str")  [return-value]

@JelleZijlstra
Copy link
Member

It does feel like a type context issue, but I think @tmke8 may be right and this is python/typeshed#10293. Unfortunately, as the typeshed PR indicates, that issue may not be easy to fix.

@AlexWaygood
Copy link
Member

It does feel like a type context issue, but I think @tmke8 may be right and this is python/typeshed#10293. Unfortunately, as the typeshed PR indicates, that issue may not be easy to fix.

(I just merged a different typeshed PR that also hopefully fixes python/typeshed#10293, but had a much less horrifying report from mypy_primer.)

@AlexWaygood
Copy link
Member

I confirmed that with python/typeshed#10293, the issue is resolved, so no mypy changes will be necessary here. The fix might not make it into mypy v1.5, but it will definitely be included in mypy v1.6 if not.

With mypy v1.4.1:

>mypy -c "x: dict[str, str] = {}; reveal_type(x.get('a') or x.get('b', 'c'))" --custom-typeshed-dir .
<string>:1: note: Revealed type is "builtins.str"
Success: no issues found in 1 source file

With mypy v1.4.1 and --custom-typeshed-dir pointing to an up-to-date clone of typeshed:

>mypy -c "x: dict[str, str] = {}; reveal_type(x.get('a') or x.get('b', 'c'))" --custom-typeshed-dir .
<string>:1: note: Revealed type is "builtins.str"
Success: no issues found in 1 source file

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

No branches or pull requests

5 participants