Search code examples
pythonpython-typingmypy

Passing on kwargs to complex functions


Consider the following attempt at add adding type hints to the functions parent and child:

def parent(*, a: Type1, b: Type2):
    ...

def child(*, c: Type3, d: Type4, **kwargs):
    parent(**kwargs)
    ...

MyPy complains that kwargs has the type Dict[str, Any] but that the arguments a and b require Type1 and Type2 respectively.

I understand that the solution to this error is to rewrite child in the following way:

def child(*, a: Type1, b: Type2, c: Type3, d: Type4, **kwargs):
    parent(a=a, b=b)
    ...

However, what if the argument list of parent is much longer, or there is function grandchild which has its own argument list and must call child. Are you required to enumerate the arguments and their types from all the downstream functions? Or is there a graceful way to handle the "per-key" typing of **kwargs?


Solution

  • With the advent of TypedDict in PEP-692 this has technically been solved:

    class ParentKwargs(TypedDict):
        a: Type1
        b: Type2
    
    def parent(**kwargs: Unpack[ParentKwargs]):
        ...
    
    class ChildKwargs(ParentKwargs):
        c: Type3
        d: Type4
    
    def child(**kwargs: Unpack[ChildKwargs]):
        ...
    

    Though perhaps not in an entirely ideal fashion - you now have to destructure the kwargs to use them and dealing with default values isn't pretty:

    class ParentKwargs(TypedDict):
        a: Type1
        b: Type2
    
    def parent(**kwargs: Unpack[ParentKwargs]):
        a = kwargs["a"]
        b = kwargs["b"]
    
    
    class OptionalChildKwargs(TypedDict, total=False):
        c: Type3
    
    class ChildKwargs(OptionalChildKwargs, ParentKwargs):
        d: Type4
    
    def child(**kwargs: Unpack[ChildKwargs]):
        c = kwargs.get(default_value)
        d = kwargs["d"]
    

    At this point there's so much visual noise that it would probably be better to just enumerate all the kwargs as you would have needed to to absent TypedDict.