diff --git a/tests/test_noqa.py b/tests/test_noqa.py new file mode 100644 index 00000000..01349097 --- /dev/null +++ b/tests/test_noqa.py @@ -0,0 +1,165 @@ +from . import check, v + +assert v # Silence pyflakes. + + +def test_noqa_attributes(v): + v.scan( + """\ +something.x = 'x' # noqa: V001 +something.z = 'z' # noqa: V006 (code for unused variable) +something.u = 'u' # noqa +""" + ) + check(v.get_unused_code(), ["z"]) + + +def test_noqa_classes(v): + v.scan( + """\ +class QtWidget: # noqa: V002 + pass + +class ABC(QtWidget): + pass # noqa: V002 (should not ignore) + +class DEF: # noqa + pass +""" + ) + check(v.get_unused_code(), ["ABC"]) + + +def test_noqa_functions(v): + v.scan( + """\ +def play(tune, instrument='bongs', _hz='50'): # noqa: V003 + pass + + +# noqa +def problems(): # noqa: V004 + ''' They don't go away. :-)''' + pass # noqa: V003 + +def hello(name): # noqa + print("Hello") +""" + ) + check(v.get_unused_code(), ["tune", "instrument", "problems"]) + + +def test_noqa_imports(v): + v.scan( + """\ +import foo +import this # noqa: V004 +import zoo +from koo import boo # noqa +from me import * +import dis # noqa: V001 (code for unused attr) +""" + ) + check(v.get_unused_code(), ["foo", "zoo", "dis"]) + + +def test_noqa_propoerties(v): + v.scan( + """\ +class Zoo: + @property # noqa + def no_of_coalas(self): + pass + + @property # noqa: V005 + def area(self, width, depth): + pass + + @property + def entry_gates(self): # noqa + pass + + @property # noqa: V003 (code for unused function) + def tickets(self): + pass +""" + ) + check( + v.get_unused_code(), + ["Zoo", "width", "depth", "entry_gates", "tickets"], + ) + + +def test_noqa_unreacahble_code(v): + v.scan( + """\ +def shave_sheeps(sheeps): # noqa: V003 + for sheep in sheeps: + if sheep.bald: + continue + sheep.grow_hair() # noqa: V006 + sheep.shave() + return sheeps + for sheep in sheeps: # noqa: V006 + if sheep.still_has_hair: + sheep.shave_again() +""" + ) + check(v.get_unused_code(), []) + + +def test_noqa_variables(v): + v.scan( + """\ +mitsi = "Mother" # noqa: V007 +harry = "Father" # noqa +shero = "doggy" # noqa: V001, V004 (code for unused import, attr) +shinchan.friend = ['masao'] # noqa: V007 +""" + ) + check(v.get_unused_code(), ["shero", "friend"]) + + +def test_noqa_with_multiple_issue_codes(v): + v.scan( + """\ +def world(axis): # noqa: V003, V006 + pass + + +for _ in range(3): + continue + xyz = hello(something, else): # noqa: V006, V007 +""" + ) + check(v.get_unused_code(), []) + + +def test_noqa_on_empty_line(v): + v.scan( + """\ +# noqa +import this +# noqa +""" + ) + check(v.get_unused_code(), ["this"]) + + +def test_noqa_with_invalid_codes(v): + v.scan( + """\ +import this # V098, A123, F876 +""" + ) + check(v.get_unused_code(), ["this"]) + + +def test_noqa_with_special_unicode(v): + v.scan( + """\ +import abc # noqa: V012, VšŸ˜Ž12 +import problems # noqa: V03šŸ™ƒ1 +""" + ) + check(v.get_unused_code(), ["abc", "problems"]) diff --git a/vulture/core.py b/vulture/core.py index 1f2fb9f3..70d71549 100644 --- a/vulture/core.py +++ b/vulture/core.py @@ -53,8 +53,8 @@ "function": "V003", "import": "V004", "property": "V005", - "variable": "V006", - "unreachable_code": "V007", + "unreachable_code": "V006", + "variable": "V007", } NOQA_REGEXP = re.compile( @@ -319,14 +319,6 @@ def get_unused_code(self, min_confidence=0, sort_by_size=False): """ Return ordered list of unused Item objects. """ - - def has_noqa(obj): - return obj.first_lineno in ( - self.noqa_matches[ERROR_CODES[obj.typ]].union( - self.noqa_matches["all"] - ) - ) - if not 0 <= min_confidence <= 100: raise ValueError("min_confidence must be between 0 and 100.") @@ -346,14 +338,12 @@ def by_size(item): + self.unreachable_code ) - reported_unused = [ - obj - for obj in unused_code - if obj.confidence >= min_confidence and not has_noqa(obj) + confidently_unused = [ + obj for obj in unused_code if obj.confidence >= min_confidence ] return sorted( - reported_unused, key=by_size if sort_by_size else by_name + confidently_unused, key=by_size if sort_by_size else by_name ) def report( @@ -504,26 +494,38 @@ def _define( confidence=DEFAULT_CONFIDENCE, ignore=None, ): + def ignored(name): + return ( + (ignore and ignore(self.filename, name)) + or _match(name, self.ignore_names) + ) + + def has_noqa(lineno, typ): + return lineno in ( + self.noqa_matches[ERROR_CODES[typ]].union( + self.noqa_matches["all"] + ) + ) + last_node = last_node or first_node typ = collection.typ - if (ignore and ignore(self.filename, name)) or _match( - name, self.ignore_names - ): + first_lineno = first_node.lineno + if ignored(name) or has_noqa(first_lineno, typ): self._log('Ignoring {typ} "{name}"'.format(**locals())) - else: - first_lineno = first_node.lineno - last_lineno = lines.get_last_line_number(last_node) - collection.append( - Item( - name, - typ, - self.filename, - first_lineno, - last_lineno, - message=message, - confidence=confidence, - ) + return + + last_lineno = lines.get_last_line_number(last_node) + collection.append( + Item( + name, + typ, + self.filename, + first_lineno, + last_lineno, + message=message, + confidence=confidence, ) + ) def _define_variable(self, name, node, confidence=DEFAULT_CONFIDENCE): self._define(