-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Describe the bug
It seems like the inferred signature of functions wrapped with @functools.wraps depends on the signature of the wrapper itself.
To be more specific, there are cases where the wrapper function should take a self argument, like so:
def wrapper(self, *args, **kwargs):
passIn this case, the signature of the wrapped function is inferred as (*args: Unknown, **kwargs: Unknown) -> Unknown.
If however, the wrapper takes only *args and **kwargs, the signature is inferred properly.
To Reproduce
Refer to the attached code.
Expected behavior
It is perhaps unclear what exactly should happen here, but if we run the same code in the python shell, and inspect its signature with the inspect.signature module method, the original signature is returned.
I therefore suggest this behavior is maintained in the typechecker.
Screenshots or Code
Version with self in wrapper:

import functools
from typing import Callable
def test_decorator(
func: Callable
):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
# Do something with self here
ret_val = func(self, *args, **kwargs)
# Do some cleanup
return ret_val
return wrapper
class TestClass:
@test_decorator
def test_func(self, arg1: int):
pass
TestClass().test_func()Version without self in wrapper:

import functools
from typing import Callable
def test_decorator(
func: Callable
):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# Do something with self here
self = None
ret_val = func(self, *args, **kwargs)
# Do some cleanup
return ret_val
return wrapper
class TestClass:
@test_decorator
def test_func(self, arg1: int):
pass
TestClass().test_func()VS Code extension or command-line
Pylance: v2022.1.3
Additional context
It is perhaps also worth it to think about other custom arguments.
For example, what happens if the wrapper's signature clearly doesn't match the wrapped function's?