nhoad / flake8-unused-arguments

Flake8 plugin to warn against unused arguments in functions
MIT License
31 stars 8 forks source link

(Feature) Ignore overridden methods in subclasses #18

Closed dargueta closed 1 year ago

dargueta commented 2 years ago

It would be great if there were a way to ignore inherited arguments in overridden methods in a subclass. For example, suppose we have the following code:

class BaseClass:
    def execute(self, context):
        ...

class MyClass(BaseClass):
    def execute(self, context):
        # Implementation doesn't use `context`

BaseClass is defined in a third-party library and I have no control over it. Currently, context is invoked by the third-party code as a keyword argument, which means that I can't name the argument _context because it'd trigger an exception.

Of course, I can use noqa: U100 on that line to suppress the error. It's tedious if you have a lot of these, but it's not a blocker. However, there's one case where we don't want to do that, when we add arguments in a compatible way:

class MyClass2(BaseClass):
    def execute(self, context, some_flag=False):
        # Implementation doesn't use `context`
        ...

Here, we don't care if the implementation doesn't use arguments defined in the superclass because we need to preserve the signature. However, we should still care about arguments that we define that aren't being used.

I'll be the first to admit this is non-trivial. The first implementation I can think of off the top of my head would require you to traverse the MRO and find the method in its parents, then compare call signatures. Proper caching will mitigate the performance impact on large codebases, but it'll still be a pain.

tmke8 commented 1 year ago

There is now a PEP to add an @override decorator to the Python standard library. If the check could ignore methods with @override then I think that would be a nice solution (even though it's not a full solution):

class BaseClass:
    def execute(self, context):
        ...

class MyClass(BaseClass):
    @override
    def execute(self, context):
        # Implementation doesn't use `context`