Skip to content

Commit

Permalink
Fix error on attributes-of-attributes in except (...): clauses (#109)
Browse files Browse the repository at this point in the history
* Fix #108, error on nested Attributes
  * Add fuzz-tests to check for crashes
  * Require attrs>=19.2.0 (this is only for hypothesmith - so could be moved to .travis.yml)
  • Loading branch information
Zac-HD authored and cooperlees committed Jan 6, 2020
1 parent 3731237 commit 238001d
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ matrix:
- python: nightly

install:
- pip install --upgrade pip setuptools coverage black
- pip install --upgrade pip setuptools coverage black hypothesmith
- pip install -e .

script:
Expand Down
17 changes: 10 additions & 7 deletions bugbear.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ def should_warn(self, code):
return False


def _to_name_str(node):
# Turn Name and Attribute nodes to strings, e.g "ValueError" or
# "pkg.mod.error", handling any depth of attribute accesses.
if isinstance(node, ast.Name):
return node.id
assert isinstance(node, ast.Attribute)
return _to_name_str(node.value) + "." + node.attr


@attr.s
class BugBearVisitor(ast.NodeVisitor):
filename = attr.ib()
Expand Down Expand Up @@ -141,13 +150,7 @@ def visit_ExceptHandler(self, node):
B001(node.lineno, node.col_offset, vars=("bare `except:`",))
)
elif isinstance(node.type, ast.Tuple):
names = []
for e in node.type.elts:
if isinstance(e, ast.Name):
names.append(e.id)
else:
assert isinstance(e, ast.Attribute)
names.append("{}.{}".format(e.value.id, e.attr))
names = [_to_name_str(e) for e in node.type.elts]
as_ = " as " + node.name if node.name is not None else ""
if len(names) == 0:
vs = ("`except (){}:`".format(as_),)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
py_modules=["bugbear"],
zip_safe=False,
python_requires=">=3.6",
install_requires=["flake8 >= 3.0.0", "attrs"],
install_requires=["flake8 >= 3.0.0", "attrs>=19.2.0"],
test_suite="tests.test_bugbear",
classifiers=[
"Development Status :: 5 - Production/Stable",
Expand Down
6 changes: 6 additions & 0 deletions tests/b013.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@
except (re.error,):
# pointless use of tuple with dotted attribute
pass

try:
pass
except (a.b.c.d, b.c.d):
# attribute of attribute, etc.
pass
27 changes: 26 additions & 1 deletion tests/test_bugbear.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import ast
import os
from pathlib import Path
import site
import subprocess
import unittest

from bugbear import BugBearChecker
from hypothesis import given
from hypothesmith import from_grammar

from bugbear import BugBearChecker, BugBearVisitor
from bugbear import (
B001,
B002,
Expand Down Expand Up @@ -262,5 +268,24 @@ def test_selfclean_test_bugbear(self):
self.assertEqual(proc.stderr, b"")


class TestFuzz(unittest.TestCase):
@given(from_grammar().map(ast.parse))
def test_does_not_crash_on_any_valid_code(self, syntax_tree):
# Given any syntatically-valid source code, flake8-bugbear should
# not crash. This tests doesn't check that we do the *right* thing,
# just that we don't crash on valid-if-poorly-styled code!
BugBearVisitor(filename="<string>", lines=[]).visit(syntax_tree)

def test_does_not_crash_on_site_code(self):
# Because the generator isn't perfect, we'll also test on all the code
# we can easily find in our current Python environment - this includes
# the standard library, and all installed packages.
for base in sorted(set(site.PREFIXES)):
for dirname, _, files in os.walk(base):
for f in files:
if f.endswith(".py"):
BugBearChecker(filename=str(Path(dirname) / f))


if __name__ == "__main__":
unittest.main()

0 comments on commit 238001d

Please sign in to comment.