python/mypy

Error handling wrappers with `**kwargs` syntax

stinodego opened this issue · 7 comments

Bug Report

mypy seems to be unable to pass down the types of **kwargs arguments properly.

To Reproduce

def my_function(flag: bool = True, **kwargs: str) -> None:
    ...


def wrapper(**kwargs: str) -> None:
    my_function(**kwargs)

Playground Link

Actual Behavior

main.py:6: error: Argument 1 to "my_function" has incompatible type "**Dict[str, str]"; expected "bool"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

Expected Behavior

I would expect no errors. Though I can see that inputting flag="hello" could cause issues, although the error message does not help in identifying this. Also, in that case, I would expect rewriting the wrapper like below would satisfy mypy (it doesn't):

def wrapper(**kwargs: str) -> None:
    if "flag" in kwargs:
        raise
    my_function(**kwargs)

Your Environment

  • Mypy version used: 1.3.0
  • Python version used: 3.11.2

Mypy is correct here. The signature of the my_function function indicates that it requires one argument of type bool, but you are not providing any such argument when calling it. If you modify your code to supply this bool value, mypy no longer emits an error.

def wrapper(**kwargs: str) -> None:
    my_function(True, **kwargs)

or

def wrapper(**kwargs: str) -> None:
    my_function(flag=True, **kwargs)

Eric is correct

I made a small error in reproducing a minimal working example. If you give flag a default value, the same error occurs (I updated the original issue). If you make it a keyword-only argument, the same error occurs.

Yes, the updated sample demonstrates a real bug in mypy. This type checks fine in pyright.

Looks like you can work around the bug by adding a positional-only separator.

def my_function(flag: bool = True, /, **kwargs: str) -> None:
    ...

Thanks for verifying! Unfortunately, having the flag positional-only is not an option for our use case.

@AlexWaygood could this be re-opened?

Not sure I agree that this is definitely a mypy bug, but I don't have time to verify/explain right now, so will reopen for now