Reputation: 2101
Apologies for the mess of a title:
Suppose I have a function defined that takes a number of parameters.
Now later in my code, I use a dictionary to pass into test()
all the keyword args.
import argparse
from typing import Dict, Callable
def test(a: str, b: Dict[str, str], c: str, d: argparse.Namespace, e: Callable) -> None:
if d.t:
print(f"{a} to the {b['foo']} to the {c}!")
else:
print(f"{a} to the {b['foo']} to the {c} also, {e()}")
def hello() -> str:
return "Hello"
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-t", action="store_true", default=False)
args = parser.parse_args()
b = {'foo': 'bar'}
info = {'b': b, "c": "foobar", "d": args, "e": hello}
test("foo", **info)
When I run MyPy on my code, I get the following errors:
test.py:21: error: Argument 2 to "test" has incompatible type "**Dict[str, object]"; expected "Dict[str, str]"
test.py:21: error: Argument 2 to "test" has incompatible type "**Dict[str, object]"; expected "str"
test.py:21: error: Argument 2 to "test" has incompatible type "**Dict[str, object]"; expected "Namespace"
test.py:21: error: Argument 2 to "test" has incompatible type "**Dict[str, object]"; expected "Callable[..., Any]"
Found 4 errors in 1 file (checked 1 source file)
Why am I getting this? And how can I properly type hint the function to allow this?
Upvotes: 0
Views: 1935
Reputation: 5877
The reason the example you had before type checks is because all values of the plot_info
are str
(homogeneous), and all keyword arguments being passed into the function are also str
. There is no possibility for type mismatch, so mypy
reports success.
In your new example, the heterogeneous types of info
cannot be encoded into its type individually. Let's see what happens when we run reveal_type
(info)
:
note: Revealed type is 'builtins.dict[builtins.str*, builtins.object*]'
So we see that mypy
has to choose a homogeneous type for the value, and it goes with object
. Can mypy
guarantee that a dictionary of objects satisfies your individual type constraints for b
, c
, d
, e
, given that your passing object
s under its eyes? Nope, so it errors.
To preserve the granular types of b
, c
, d
, e
, we can use TypedDict
, which was made to support heterogeneously-typed values in Dict
s:
class Info(TypedDict):
b: Dict[str, str]
c: str
d: argparse.Namespace
e: Callable
and change the info
declaration to:
info: Info = {'b': b, "c": "foobar", "d": args, "e": hello}
This causes mypy
to report success.
We have to duplicate the argument names and types. Sadly, Generate TypedDict from function's keyword arguments shows no direct workaround for this.
Upvotes: 3