Skip to content

Commit

Permalink
Differentiate between functions and methods.
Browse files Browse the repository at this point in the history
  • Loading branch information
jendrikseipp committed Jun 2, 2020
1 parent dadb352 commit 331118f
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 13 deletions.
9 changes: 7 additions & 2 deletions tests/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ def bar(self):
@property
def myprop(self):
pass
def myfunc():
pass
"""


Expand All @@ -39,11 +42,12 @@ def test_item_report(check_report):
expected = """\
{filename}:1: unused import 'foo' (90% confidence)
{filename}:3: unused class 'Foo' (60% confidence)
{filename}:7: unused function 'bar' (60% confidence)
{filename}:7: unused method 'bar' (60% confidence)
{filename}:8: unused attribute 'foobar' (60% confidence)
{filename}:9: unused variable 'foobar' (60% confidence)
{filename}:11: unreachable code after 'return' (100% confidence)
{filename}:13: unused property 'myprop' (60% confidence)
{filename}:17: unused function 'myfunc' (60% confidence)
"""
check_report(mock_code, expected)

Expand All @@ -52,10 +56,11 @@ def test_make_whitelist(check_report):
expected = """\
foo # unused import ({filename}:1)
Foo # unused class ({filename}:3)
bar # unused function ({filename}:7)
_.bar # unused method ({filename}:7)
_.foobar # unused attribute ({filename}:8)
foobar # unused variable ({filename}:9)
# unreachable code after 'return' ({filename}:11)
_.myprop # unused property ({filename}:13)
myfunc # unused function ({filename}:17)
"""
check_report(mock_code, expected, make_whitelist=True)
24 changes: 19 additions & 5 deletions tests/test_scavenging.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,10 @@ async def bar(self):
"""
)
check(v.defined_classes, ["Foo"])
check(v.defined_funcs, ["bar"])
check(v.defined_funcs, [])
check(v.defined_methods, ["bar"])
check(v.unused_classes, ["Foo"])
check(v.unused_funcs, ["bar"])
check(v.unused_methods, ["bar"])


def test_function_and_method1(v):
Expand All @@ -114,9 +115,11 @@ def func():
"""
)
check(v.defined_classes, ["Bar"])
check(v.defined_funcs, ["func", "func"])
check(v.defined_funcs, ["func"])
check(v.defined_methods, ["func"])
check(v.unused_classes, ["Bar"])
check(v.unused_funcs, [])
check(v.unused_methods, ["func"])


def test_attribute1(v):
Expand Down Expand Up @@ -162,7 +165,8 @@ def foo(self):
)
check(v.used_attrs, ["foo"])
check(v.defined_classes, ["Bar"])
check(v.defined_funcs, ["foo"])
check(v.defined_funcs, [])
check(v.defined_methods, ["foo"])
check(v.unused_classes, [])
check(v.unused_funcs, [])

Expand Down Expand Up @@ -273,12 +277,22 @@ def __init__(self):
class Bar(object):
def foo(self):
pass
@classmethod
def bar(cls):
pass
@staticmethod
def foobar():
pass
"""
)
check(v.defined_classes, ["Bar"])
check(v.defined_funcs, ["foo"])
check(v.defined_funcs, [])
check(v.defined_methods, ["foo", "bar", "foobar"])
check(v.unused_classes, ["Bar"])
check(v.unused_funcs, [])
check(v.unused_methods, ["bar", "foobar"])


def test_token_types(v):
Expand Down
48 changes: 42 additions & 6 deletions vulture/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,16 @@
if sys.version_info < (3, 4):
IGNORED_VARIABLE_NAMES |= {"True", "False"}

# TODO: reorganize codes.
ERROR_CODES = {
"attribute": "V101",
"class": "V102",
"function": "V103",
"import": "V104",
"method": "V107",
"property": "V105",
"unreachable_code": "V201",
"variable": "V106",
"unreachable_code": "V201",
}


Expand Down Expand Up @@ -95,8 +97,12 @@ def _ignore_import(filename, import_name):


def _ignore_function(filename, function_name):
return _is_special_name(function_name) or (
function_name.startswith("test_") and _is_test_file(filename)
return function_name.startswith("test_") and _is_test_file(filename)


def _ignore_method(filename, method_name):
return _is_special_name(method_name) or (
method_name.startswith("test_") and _is_test_file(filename)
)


Expand Down Expand Up @@ -171,7 +177,9 @@ def get_whitelist_string(self):
self.message, filename, self.first_lineno
)
else:
prefix = "_." if self.typ in ["attribute", "property"] else ""
prefix = ""
if self.typ in ["attribute", "method", "property"]:
prefix = "_."
return "{}{} # unused {} ({}:{:d})".format(
prefix, self.name, self.typ, filename, self.first_lineno
)
Expand Down Expand Up @@ -207,6 +215,7 @@ def get_set(typ):
self.defined_classes = get_list("class")
self.defined_funcs = get_list("function")
self.defined_imports = get_list("import")
self.defined_methods = get_list("method")
self.defined_props = get_list("property")
self.defined_vars = get_list("variable")
self.unreachable_code = get_list("unreachable_code")
Expand Down Expand Up @@ -312,6 +321,7 @@ def by_size(item):
+ self.unused_classes
+ self.unused_funcs
+ self.unused_imports
+ self.unused_methods
+ self.unused_props
+ self.unused_vars
+ self.unreachable_code
Expand Down Expand Up @@ -360,6 +370,10 @@ def unused_imports(self):
self.defined_imports, self.used_names | self.used_attrs
)

@property
def unused_methods(self):
return _get_unused_items(self.defined_methods, self.used_attrs)

@property
def unused_props(self):
return _get_unused_items(self.defined_props, self.used_attrs)
Expand Down Expand Up @@ -557,7 +571,26 @@ def visit_FunctionDef(self, node):
utils.get_decorator_name(decorator)
for decorator in node.decorator_list
]
typ = "property" if "@property" in decorator_names else "function"

first_arg = None
if node.args.args:
try:
first_arg = node.args.args[0].arg
except AttributeError:
# Python 2.7
first_arg = node.args.args[0].id

if "@property" in decorator_names:
typ = "property"
elif (
"@staticmethod" in decorator_names
or "@classmethod" in decorator_names
or first_arg == "self"
):
typ = "method"
else:
typ = "function"

if any(
_match(name, self.ignore_decorators) for name in decorator_names
):
Expand All @@ -568,8 +601,11 @@ def visit_FunctionDef(self, node):
)
elif typ == "property":
self._define(self.defined_props, node.name, node)
elif typ == "method":
self._define(
self.defined_methods, node.name, node, ignore=_ignore_method
)
else:
# Function is not a property.
self._define(
self.defined_funcs, node.name, node, ignore=_ignore_function
)
Expand Down

0 comments on commit 331118f

Please sign in to comment.