Search code examples
pythonpycharmdecoratorpython-typing

Decorated function call now showing warning for incorrect arguments in PyCharm


I'm having some problems when static type checking decorated functions. For instance, when I use an incorrect function argument name or type, I don't get any warning or error hints in the IDE, only at runtime.

What steps will reproduce the problem?

  • Add a decorator to a function

  • Use a incorrect argument name or type

What is the expected result?

  • PyCharm should highlight the wrong argument name or wrong type.

What happens instead?

  • The attribute name is not highlighted.

Additional Info

  • This is with Python 3.10 and PyCharm 2023.3.4

Code sample follows:

def myDecorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)

    return wrapper

@myDecorator
def myFunc(x: int, y: int):
    print(x+y)

myFunc(z=4, x="something")  # Expecting "z=4" and x="something" to be highlighted

Edit

It's now working with regular functions. However, I'm encountering the same problem if these functions are defined inside of a Class, as seen below:

from typing import ParamSpec
from typing import TypeVar
from typing import Callable

P = ParamSpec("P")
T = TypeVar("T")


def myDecorator(func: Callable[P, T]) -> Callable[P, T]:
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
        return func(*args, **kwargs)
    return wrapper

class myClass:
    @myDecorator
    def myFunc2(self, x: int, y: int) -> None:
        print(x + y)

    @myDecorator
    def another_function(self):
        self.myFunc2(x=3)  # showing type hinting: P and highlighting "x=3" as a wrong argument.

Solution

  • Looks similar to this PyCharm issue.

    If you are willing to annotate the decorator with ParamSpec you can do ...

    from typing import ParamSpec
    from typing import TypeVar
    from typing import Callable
    
    P = ParamSpec("P")
    T = TypeVar("T")
    
    
    def myDecorator(func: Callable[P, T]) -> Callable[P, T]:
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
            return func(*args, **kwargs)
        return wrapper
    
    
    @myDecorator
    def myFunc(x: int, y: int) -> None:
        print(x + y)
    
    
    myFunc(z=4, x="something")  # Expecting "z=4" and x="something" to be highlighted
    

    This way PyCharm will report the mistake.