Repository navigation
mypy shows type error with json.dump(s) and kwargs #8772
Description
Activity
Same happens when passing
**kwargsto a class constructor in Python3.7 with same version ofmypy:from typing import Dict class A: """Class A.""" def __init__(self, a: str, b: str, c: str, d: int = 2) -> None: """Construct A object.""" self.a: str = a self.b: str = b self.c: str = c self.d: int = d def main() -> None: """Run main method.""" argument_a: str = "argument_a" arguments_b_c: Dict[str, str] = {"b": "argument_b", "c": "argument_c"} object_a: A = A(a=argument_a, **arguments_b_c) print(object_a) if __name__ == "__main__": main()
Gives the error:
> mypy a.py a.py:21: error: Argument 2 to "A" has incompatible type "**Dict[str, str]"; expected "int" Found 1 error in 1 file (checked 1 source file)Edit: Added error.
Reacted by Conor SheehanI feel like there's two issues here (neither of which is directly related to
jsonor class constructors, and neither of which is necessarily a bug):- mypy by default infers a type like
Dict[str, object]for a heterogeneous dict. This is a reasonable inference, but sometimes a TypedDict may better. To get a TypedDict type you need to explicitly declare it. - mypy is very strict about the type of dicts passed as
**kwargsto a function. This is type-safe in theory but means the type of a**kwargsdict in most practical cases has to beDict[str, Any]or a TypedDict.
- mypy by default infers a type like
To elaborate on the last point (and to check that I understand the semantics here), I believe that mypy is enforcing that the type of the value of the expanded dict must match every possible keyword argument to the function called. Therefore, if the function's keyword arguments take incompatible types, only a dict whose values are typed as
Any(or aTypedDictwhere mypy knows the types of the values of specific keys) is permitted.I was instead expecting mypy to err on the side of generosity and instead assume that if the value type of the dict matches the type of any of the keyword arguments of the function, the caller probably knows what they're doing.
I'm now not sure which approach is better. The approach I was expecting is less safe, but mypy's current approach is going to result in a lot of false positives. I suppose the workaround of typing the dict with
Anyvalues isn't too bad. Setting up aTypedDicthere is a bit tedious, although obviously the most correct.Yeah, my inclination here is to say not-a-bug. Though if somebody wants to open a bug to argue for looser interpretation of kwargs typing, that would be reasonable.
Would it be possible to mention this directly in the documentation under common issues? I've seen multiple people get confused by this behavior, and it can be quite difficult to understand what's happening because of the way the type mismatch is reported.
Reacted by dliu-fn and Conor SheehanAdded #8874 about improving the generated error messages.
I would argue to lean towards less safe, but assuming the caller probably knows what they're doing. Ask forgiveness, not permission.
I think aspects of this behaviour have been changed on master, so if you have complaints, please make sure they're up to date.
- added a commit that references this issue
on Mar 20, 2022
I have noticed that in the seemingly common situation of passing
**kwargstojson.dumporjson.dumps,mypyfails the type checks unless the kwargs are explicitly annotated asDict[str, Any]. A simple example:Which results in:
If I change the example to this:
The errors are the same, except the inferred type changes to
**Dict[str, object]. I can work around it like so:Using Python 3.8 and
mypy == 0.770on Arch Linux. The error is still present when using the current master (commit 2a3de7b).I have also tested this with
pytypeandpytypedoes not have the same failure.