Skip to content

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

Closed

Description

@hfcredidio

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

Activity

  1. tmke8 commented on Jun 27, 2023

    @tmke8
    Contributor

    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.

  2. ikonst commented on Jun 27, 2023

    @ikonst
    Contributor

    @AlexWaygood topic-type-context?

  3. ikonst commented on Jun 27, 2023

    @ikonst
    Contributor

    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]
  4. JelleZijlstra commented on Jun 27, 2023

    @JelleZijlstra
    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.

  5. AlexWaygood commented on Jul 26, 2023

    @AlexWaygood
    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.)

  6. AlexWaygood commented on Jul 28, 2023

    @AlexWaygood
    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions