Skip to content

Commit

Permalink
VultureBear: Make use of the new vulture API
Browse files Browse the repository at this point in the history
The new vulture API provides better integration with coala. It
natively supports `item.confidence`. Also, it gives precise
information about the location of dead code (`first_line, `last_line`)
  • Loading branch information
RJ722 committed Aug 29, 2017
1 parent 1347da8 commit 58a3999
Show file tree
Hide file tree
Showing 15 changed files with 114 additions and 103 deletions.
3 changes: 2 additions & 1 deletion .coafile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[Default]
files = *.py, bears/**/*.py, tests/**/*.py, bears/*.py.in
ignore = tests/python/test_files/pylint_test.py, tests/python/bandit_test_files/*
ignore = tests/python/test_files/pylint_test.py, tests/python/bandit_test_files/*,
tests/python/vulture_test_files/*

max_line_length = 79
use_spaces = True
Expand Down
2 changes: 1 addition & 1 deletion bear-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ rstcheck~=3.1
safety~=0.5.1
scspell3k~=2.0
vim-vint~=0.3.12
vulture~=0.14.0
vulture~=0.25.0
yamllint~=1.6.1
yapf~=0.16.0
28 changes: 6 additions & 22 deletions bears/python/VultureBear.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,26 @@
from dependency_management.requirements.PipRequirement import PipRequirement
from vulture import Vulture

CONFIDENCE_MAP = {
'attribute': 70,
'class': 70,
'function': 70,
'import': 95,
'property': 70,
'variable': 70,
}


def _find_unused_code(filenames):
"""
:param filenames: List of filenames to check.
:return: Generator of Result objects.
"""

def file_lineno(item):
return (item.filename.lower(), item.lineno)

vulture = Vulture()
vulture.scavenge(filenames)
for item in sorted(
vulture.unused_funcs + vulture.unused_imports +
vulture.unused_props + vulture.unused_vars +
vulture.unused_attrs, key=file_lineno):
message = 'Unused {0}: {1}'.format(item.typ, item)
for item in vulture.get_unused_code():
yield Result.from_values(origin='VultureBear',
message=message,
message=item.message,
file=item.filename,
line=item.lineno,
confidence=CONFIDENCE_MAP[item.typ])
line=item.first_lineno,
end_line=item.last_lineno,
confidence=item.confidence)


class VultureBear(GlobalBear):
LANGUAGES = {'Python', 'Python 3'}
REQUIREMENTS = {PipRequirement('vulture', '0.14.0')}
REQUIREMENTS = {PipRequirement('vulture', '0.25.0')}
AUTHORS = {'The coala developers'}
AUTHORS_EMAILS = {'[email protected]'}
LICENSE = 'AGPL-3.0'
Expand Down
142 changes: 63 additions & 79 deletions tests/python/VultureBearTest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import unittest
from queue import Queue
from textwrap import dedent
Expand All @@ -9,6 +10,15 @@
from bears.python.VultureBear import VultureBear


def get_testfile_path(name):
return os.path.join(os.path.dirname(__file__), 'vulture_test_files', name)


def load_testfile(name):
with open(get_testfile_path(name)) as f:
return f.read()


class VultureBearTest(unittest.TestCase):

def setUp(self):
Expand Down Expand Up @@ -42,85 +52,59 @@ def prep_file():

return list(self.uut.run())

def verify_results(self, test_file, expected):
detected = dict((item.message, (item.affected_code[0].start.line,
item.affected_code[0].end.line,
item.confidence))
for item in self.get_results(load_testfile(test_file)))
self.assertEqual(detected, expected)

def test_used_variable(self):
good_file = """
x = 2
print(x)
"""
self.assertEqual(len(self.get_results(good_file)), 0)
self.verify_results('used_variable.py', {})

def test_unused_variable(self):
bad_file = """
b = 10
a = 12
print(a)
"""
self.assertEqual(len(self.get_results(bad_file)), 1)

def test_unused_parameter(self):
bad_file = """
def test(a):
return 1
test(1)
"""
self.assertEqual(len(self.get_results(bad_file)), 1)

def test_unused_function(self):
bad_file = """
def test(a):
return a
"""
self.assertEqual(len(self.get_results(bad_file)), 1)

def test_import_confidence(self):
bad_file = """
from os import *
import subprocess
"""
result = self.get_results(bad_file)
self.assertEqual(len(result), 1)
self.assertEqual(result[0].confidence, 95)

def test_class_confidence(self):
bad_file = """
class Name:
def __init__(self, name):
self.name = name
self.sur_name = 'Something'
"""
result = self.get_results(bad_file)
self.assertEqual(len(result), 2)
self.assertEqual(result[0].confidence, 70)

def test_func_confidence(self):
bad_file = """
def hello(name):
print('Hello World')
"""
result = self.get_results(bad_file)
self.assertEqual(len(result), 2)
self.assertEqual(result[0].confidence, 70)

def test_prop_confidence(self):
bad_file = """
class Bar(object):
@property
def prop(self):
pass
c = Bar()
"""
result = self.get_results(bad_file)
self.assertEqual(len(result), 2)
self.assertEqual(result[0].confidence, 70)

def test_var_confidence(self):
bad_file = """
name = 'Foo Bar'
"""
result = self.get_results(bad_file)
self.assertEqual(len(result), 1)
self.assertEqual(result[0].confidence, 70)
self.verify_results('unused_variable.py', {
"unused variable 'b'": (1, 1, 60)
})

def test_unused_arg(self):
self.verify_results('unused_arg.py', {
"unused variable 'a'": (1, 1, 100)
})

def test_import(self):
self.verify_results('unused_import.py', {
"unused import 'subprocess'": (2, 2, 90)
})

def test_class(self):
self.verify_results('unused_class.py', {
"unused class 'Name'": (1, 5, 60),
"unused attribute 'name'": (4, 4, 60),
"unused attribute 'surname'": (5, 5, 60)
})

def test_function(self):
self.verify_results('unused_function.py', {
"unused function 'hello'": (1, 2, 60)
})

def test_property(self):
self.verify_results('unused_property.py', {
"unused property 'prop'": (3, 5, 60)
})

def test_unsatisfiable_while(self):
self.verify_results('unsatisfiable_while.py', {
"unsatisfiable 'while' condition": (5, 7, 100)
})

def test_unsatisfiable_if(self):
self.verify_results('unsatisfiable_if.py', {
"unsatisfiable 'if' condition": (1, 2, 100)
})

def test_unreachable_else(self):
self.verify_results('unreachable_else.py', {
"unreachable 'else' block": (3, 6, 100)
})
Empty file.
6 changes: 6 additions & 0 deletions tests/python/vulture_test_files/unreachable_else.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
if True:
print('Reachable')
elif something():
print('Never executed')
else:
print('Unreachable')
2 changes: 2 additions & 0 deletions tests/python/vulture_test_files/unsatisfiable_if.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
if False:
print('Unreachable')
7 changes: 7 additions & 0 deletions tests/python/vulture_test_files/unsatisfiable_while.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
while True:
print('I am reachable.')
break

while 0:
print('I am unreachable.')
print('I can be detected.')
5 changes: 5 additions & 0 deletions tests/python/vulture_test_files/unused_arg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def func(a):
pass


func(1)
5 changes: 5 additions & 0 deletions tests/python/vulture_test_files/unused_class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Name:

def __init__(self, name):
self.name = name
self.surname = 'Something'
2 changes: 2 additions & 0 deletions tests/python/vulture_test_files/unused_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def hello(name):
print('Hello', name)
2 changes: 2 additions & 0 deletions tests/python/vulture_test_files/unused_import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from os import *
import subprocess
8 changes: 8 additions & 0 deletions tests/python/vulture_test_files/unused_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class Bar(object):

@property
def prop(self):
pass


Bar()
3 changes: 3 additions & 0 deletions tests/python/vulture_test_files/unused_variable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
b = 10
a = 12
print(a)
2 changes: 2 additions & 0 deletions tests/python/vulture_test_files/used_variable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
x = 2
print(x)

0 comments on commit 58a3999

Please sign in to comment.