jendrikseipp / vulture

Find dead Python code
MIT License
3.42k stars 150 forks source link

methods called from decorator incorrectly reported #199

Closed dmcnulla closed 4 years ago

dmcnulla commented 4 years ago
from decorator.py import mydectorator

class myclass:
    @my_decorator
    def caller():
        pass

    def starter():
        do_something()
from functools import wraps:
def mydecorator():
    @wraps(func)
    def inner():
        self.starter()

    return inner

The decorator code is in a different path than I am checking. In this case, starter() is reported as potentially unused. I can cognitively solve that, but maybe you could extend to catch that? This is similar to #7, sorry if it's a duplicate.

BTW, awesome library, I love it.

BTW2, I removed the line numbers in the whitelist, still works.

jendrikseipp commented 4 years ago

Thanks for the report! I think your example contains some typos (or are they intentional?). When I change it to

from decorator import mydecorator

class myclass:
    @mydecorator
    def caller():
        pass

    def starter():
        do_something()

from functools import wraps
def mydecorator():
    @wraps(func)
    def inner():
        self.starter()

    return inner

I get

testme.py:3: unused class 'myclass' (60% confidence)
testme.py:4: unused function 'caller' (60% confidence)

What exactly did you want to report?

BTW2, I removed the line numbers in the whitelist, still works.

That's intentional.

RJ722 commented 4 years ago

from functools import wraps def mydecorator(): @wraps(func) def inner(): self.starter()

return inner

I haven't tested this, but are you sure that this doesn't give an error when run? I don't think that use of self would be allowed here (or, is it?).

RJ722 commented 4 years ago

I looked into this. It's possible this way:

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def inner(self, *args, **kwargs):
        self.starter()
        return func(self, *args, **kwargs)

    return inner

class MyClass:
    @my_decorator
    def caller(self):
        print("in caller")

    def starter(self):
        print("in starter")

MyClass().caller()

And running Vulture runs fine on this -- no output (as expected).

RJ722 commented 4 years ago

Which, now I think of it, provides scope for a false negative -- If I add another class with a method named starter, to the above file:

class YourClass:
    def starter(self):
        pass

YourClass()

Vulture still produces no output. My hunch is that using self in inner somehow leaks all attributes in global scope -- but would need to look into this more (which I'd be happy to, but in some time).

jendrikseipp commented 4 years ago

Remember that Vulture doesn't know about scopes. It only knows about defined and used names.

RJ722 commented 4 years ago

Yes, I do in fact stand corrected. Thanks.

@dmcnulla do you have more input from your side -- we ran Vulture on a slightly altered version of the code you provided and ensured that Vulture works as expected.

jendrikseipp commented 4 years ago

Feel free to reopen if the issue persists.

dmcnulla commented 4 years ago

Thanks, I wasn't paying attention. I'll try the suggestions/alternates.