Search code examples
pythonpython-typingmypy

Why does mypy have a hard time with assignment to nested dicts?


mypy version 0.910

Consider

d = {
    'a': 'a',
    'b': {
        'c': 1
    }
}

d['b']['d'] = 'b'

Feeding this to mypy results with

error: Unsupported target for indexed assignment ("Collection[str]")

Putting a side that mypy inferred the wrong type for d (it is clearly not a collection of strings), adding a very basic explicit type for d fixes this:

d: dict = {
    ... # same as above
}

Success: no issues found in 1 source file

I find this very peculiar. mypy should definitely be able to infer that d is a dict without d: dict.


Solution

  • d is not being inferred as a collection of strings. It is being inferred as a dict, but dicts take two type variables, one for the keys and one for the values. If we use reveal_type:

    d = {
        'a': 'a',
        'b': {
            'c': 1
        }
    }
    reveal_type(d)
    d['b']['d'] = 'b'
    

    I get:

    (py39) jarrivillaga-mbp16-2019:~ jarrivillaga$ mypy --version
    mypy 0.910
    (py39) jarrivillaga-mbp16-2019:~ jarrivillaga$ mypy scratch.py
    scratch.py:7: note: Revealed type is "builtins.dict[builtins.str*, typing.Collection*[builtins.str]]"
    scratch.py:8: error: Unsupported target for indexed assignment ("Collection[str]")
    Found 1 error in 1 file (checked 1 source file)
    

    So, it is being inferred as: builtins.dict[builtins.str*, typing.Collection*[builtins.str]] which is a dict mapping strings to collections of strings. This is because for your dict, you have used str and dict[str, int] as values, and I can only surmise that typing.Collection[str] is the least broad type that encompasses str and dict[str, str] ha both. I'm not really sure how mypy is supposed to handle the inference of nested dict literals like yours.

    Note, annotating with just dict means it is going to use dict[Any, Any], which you probably don't want. You should try to give a more constrained type, but that depends on how you intend to use d.