typing/mypy: difference between dict[key] and dict.get(key)?

Viewed 371

Here's my test script:

class A:
    def __init__(self, config: dict) -> None:
        self.config = config


def test_func(a: int) -> None:
    pass


main_config = {"a": "x"}
a = A(config=main_config)

# value direct
test_func(1)  # line 14
test_func("b")  # line 15

# value with wrong type from dict
test_func(main_config["a"])  # line 18
test_func(main_config.get("a"))  # line 19

# value with wrong type from dict in class instance
test_func(a.config["a"])  # line 22
test_func(a.config.get("a"))  # line 23

If I test it with mypy (0.910) then I get the following result:

> mypy test.py                                                                                                                                                                                                                                           
test.py:15: error: Argument 1 to "test_func" has incompatible type "str"; expected "int"
tests.py:18: error: Argument 1 to "test_func" has incompatible type "str"; expected "int"
tests.py:19: error: Argument 1 to "test_func" has incompatible type "Optional[str]"; expected "int"
tests.py:23: error: Argument 1 to "test_func" has incompatible type "Optional[Any]"; expected "int"
Found 4 errors in 1 file (checked 1 source file)


Why does mypy miss/not report the call on line 22?

1 Answers

Just an assumption:

dict.__getitem__() (a.k.a a.config[...]) does not have type annotation. While dict.get has:

def get(self, key: _KT) -> Optional[_VT_co]: ...

Simple proof of my assumption:

from typing import Optional, Any


def test_func(a: int) -> None:
    pass

def foo(a):
    if a == 1:
        return 123
    elif a == 2:
        return 'asd'
    else:
        raise ValueError


def foo_typed(a) -> Optional[Any]:
    if a == 1:
        return 123
    elif a == 2:
        return 'asd'
    else:
        return None


test_func(foo(2))
test_func(foo_typed(2)) # line 26

Produces only:

main.py:26: error: Argument 1 to "test_func" has incompatible type "Optional[Any]"; expected "int"

Even though foo(2) returns string.

Related