Skip to content

functools.wraps inferred signature breaks with non-standard wrapper arguments #3939

@Dzeri96

Description

@Dzeri96

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):
    pass

In 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:
image

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:
image

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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    as designedNot a bug, working as intended

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions