How to deal with "incompatible type "Optional[str]"; expected "str""?

Question:

Let’s say I have a code structure like this

from __future__ import annotations
from typing import TypedDict


class ValDict(TypedDict):
    something: str
    a: A


class A:
    def __init__(self, x: str) -> None:
        if x and isinstance(x, str):
            self.x = x
        else:
            raise ValueError("x has to be a non-empty string")


class B:
    def __init__(self, something: str, a: A) -> None:
        self.something = something
        if isinstance(a, A):
            self.a = a
        else:
            raise ValueError("a has to be of type A")

    @classmethod
    def from_dict(cls, stuff: ValDict) -> B:
        something = stuff.get('something')
        a = stuff.get('a')

        return cls(something, a)

and I run mypy on this, I will receive 2 errors

error: Argument 1 to "B" has incompatible type "Optional[str]"; expected "str"

error: Argument 2 to "B" has incompatible type "Optional[A]"; expected "A"

The errors are quite clear: As .get can also return None, I might not pass the correct types to the cls call in my from_dict method.

My question is how to avoid it. For the argument something I could potentially get away with modifying to

something = stuff.get('something', '')

but how would I handle the case for a? Any ideas?

Asked By: Cleb

||

Answers:

I would simply drop the use of get entirely and just access the keys directly. Sure, this raises a KeyError, but the class would fail to initialize properly anyway if you pass in anything other than an instance of A.

You could have a "default instance" of A to use whenever it isn’t present in stuff, but judging from your B.__init__ you don’t seem to want default parameters.

So the method would become:

@classmethod
def from_dict(cls, stuff: ValDict) -> B:
    something = stuff['something']
    a = stuff['a']

    return cls(something, a)

This way, three things can happen:

  • stuff has the proper keys and values -> B is initialized
  • stuff does not have the proper keys -> KeyError on B.from_dict
  • stuff has the proper keys, but key "a"‘s value is not of the proper type -> ValueError on B.__init__
Answered By: jfaccioni

mypy is very distrustful, so some assertions of parameters before they are passed in can help:

arg_1 = "something"
arg_2 = A()

assert isistance(arg_1, str)
assert isistance(arg_2, A)

# and call your code without linting failing with an incompatible type warning.
b = B(arg_1, arg_2)

Edit: I see the assertion are in the class, however mypy would like to be sure of the type BEFORE they are passed to the class.

Answered By: NonCreature0714
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.