There is a fairly recent proposal for a mechanism allowing TypedDict
to be used to annotate **kwargs
in PEP 692. However the discussion is still ongoing AFAIK and there are some valid arguments against this.
One such argument is the lack of strong motivation for such functionality. In other words, why would you really need this? Take your case for example. If you already have a typed dictionary defined somewhere and you know the function must take such a dictionary as its argument(s), why not just define a regular function parameter in the form of such a dictionary:
from typing import TypedDict
class SpecialDict(TypedDict, total=False):
a: int
b: int
def func(d: SpecialDict) -> None:
print(d)
if __name__ == "__main__":
func({"a": 1})
func({"b": 1})
func({"c": 1})
Causes mypy
to complain about the last line with the following:
error: Extra key "c" for TypedDict "SpecialDict"
Inside the function keyword-arguments are treated like a dictionary anyway, so I don't really see the benefit of constructing your function with them as opposed to a normal dictionary. Especially since **kwargs
indicate flexibility that you don't really seem to want.
Besides that, there are a number of problems with your requirements. If you define a function like this def f(**kwargs): ...
, it takes only keyword arguments and no positional arguments. Meaning none of these will work, regardless of how you annotate the function:
func(2)
func('2')
func([2])
Also, you wanted some type annotation to cause a TypeError
, which will never happen by itself because type annotations are mostly for the benefit of static type checkers. The Python interpreter doesn't care about them at all.
If you do want it to care, you'll have to implement the logic for checking types at runtime yourself.
I would advise to adhere to the Zen of Python and be more explicit. If you really want your function to accept only specific keyword arguments, define it accordingly:
def func(*, a: int, b: int) -> None:
...
You can then still call it by unpacking a dictionary with the right keys, if you want:
d = {"a": 1, "b": 2}
func(**d)
Hope this helps.