batshitlazy
batshitlazy

Reputation: 81

mypy error: error: Value of type "Optional[Dict[Any, Any]]" is not indexable

I have a small function which I've typohinted, but I get a mypy error. I looked it up and found some suggestions regarding casting and adding assertion but they don't help either. I'm using mypy==0.812 and Python 3.6. Any hints on how to fix this would help.

error: Value of type "Optional[Dict[Any, Any]]" is not indexable

This is the function:

from typing import Union
from datetime import datetime

def _get_number(my_dict: Union[dict, None], relevant_key: Union[str, None]) -> int:
    if my_dict is None:
        return 0
    else:
        # my_dict: dict = cast(dict, my_dict) # adding this didn't help with the mypy error
        # assert isinstance(my_dict, dict) # adding this didn't help with the mypy error
        sorted_keys = sorted(
            my_dict,
            key=lambda k: datetime.strptime(my_dict[k], "%H:%M").time(),
        )
        return sorted_keys.index(relevant_key)

Upvotes: 3

Views: 8979

Answers (2)

MichaelD
MichaelD

Reputation: 1326

While @kaya3 is correct, it quite defeats the use of a lambda function to have to specify all of this just to satisfy type checking. I would offer the alternative to just let mypy skip this line with an ignore pragma.

sorted_keys = sorted(
            my_dict,
            key=lambda k: datetime.strptime(my_dict[k], "%H:%M").time(),
        ) #type: ignore

Upvotes: 0

kaya3
kaya3

Reputation: 51102

The problem is that although my_dict is known to be a dictionary where the lambda is declared, MyPy is not smart enough to know it will still be a dictionary when the lambda is called. In the general case, MyPy is correct to label this as an error:

def test(d: Optional[dict]) -> None:
    if d is not None:
        # d is definitely a dict where the lambda is declared
        f = lambda: print(d['foo'])
        d = None
        # now d is None when the lambda is called
        f()

The difference between your code and this example is that there is no opportunity for my_dict to become None in between the lambda being declared and it being called, because sorted calls the lambda immediately and then discards it. But MyPy doesn't know that, so it conservatively labels your code with the same error.

The solution is to use a variable of type dict instead of Optional[dict]. You can achieve this simply by declaring a new variable like my_actual_dict: dict = my_dict just after checking that my_dict isn't None, and then use my_actual_dict in the lambda. Since my_actual_dict has the static type dict, MyPy knows it can't be assigned the value None after the lambda is declared.

Upvotes: 4

Related Questions