Skip to content

Commit

Permalink
B001: do not use bare except
Browse files Browse the repository at this point in the history
  • Loading branch information
ambv committed Apr 13, 2016
1 parent f72c90f commit 4980cc5
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 2 deletions.
2 changes: 0 additions & 2 deletions README.md

This file was deleted.

58 changes: 58 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
==============
flake8-bugbear
==============

A plugin for flake8 finding likely bugs and design problems in your
program. Contains warnings that don't belong in pyflakes and pep8::

bug·bear (bŭg′bâr′)
n.
1. A cause of fear, anxiety, or irritation: *Overcrowding is often
a bugbear for train commuters.*
2. A difficult or persistent problem: *"One of the major bugbears of
traditional AI is the difficulty of programming computers to
recognize that different but similar objects are instances of the
same type of thing" (Jack Copeland).*
3. A fearsome imaginary creature, especially one evoked to frighten
children.

List of warnings
----------------

B001
~~~~

Do not use bare ``except:``, it also catches unexpected events " "like
memory errors, interrupts, system exit, and so on. Prefer ``except
Exception:``. If you're sure what you're doing, be explicit and write
``except BaseException:``.

Tests
-----

Just run::

python setup.py test


License
-------

MIT


Change Log
----------

16.4.0
~~~~~~

* first published version

* date-versioned


Authors
-------

Glued together by `Łukasz Langa <mailto:[email protected]>`_.
89 changes: 89 additions & 0 deletions bugbear.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import ast
from collections import namedtuple
from functools import partial

import attr
import pep8


__version__ = '16.4.0'


@attr.s
class BugBearChecker(object):
name = 'flake8-bugbear'
version = __version__

tree = attr.ib(default=None)
filename = attr.ib(default='(none)')
builtins = attr.ib(default=None)
lines = attr.ib(default=None)
visitor = attr.ib(default=attr.Factory(lambda: BugBearVisitor))

def run(self):
if not self.tree or not self.lines:
self.load_file()
visitor = self.visitor(
filename=self.filename,
lines=self.lines,
)
visitor.visit(self.tree)
for e in visitor.errors:
if pep8.noqa(self.lines[e.lineno - 1]):
continue

yield e

def load_file(self):
"""Loads the file in a way that auto-detects source encoding and deals
with broken terminal encodings for stdin.
Stolen from flake8_import_order because it's good.
"""

if self.filename in ("stdin", "-", None):
self.filename = "stdin"
self.lines = pep8.stdin_get_value().splitlines(True)
else:
self.lines = pep8.readlines(self.filename)

if not self.tree:
self.tree = ast.parse("".join(self.lines))


@attr.s
class BugBearVisitor(ast.NodeVisitor):
filename = attr.ib()
lines = attr.ib()
node_stack = attr.ib(default=attr.Factory(list))
errors = attr.ib(default=attr.Factory(list))

if False:
# Useful for tracing what the hell is going on.

def __getattr__(self, name):
print(name)
return self.__getattribute__(name)

def visit(self, node):
self.node_stack.append(node)
super().visit(node)
self.node_stack.pop()

def visit_ExceptHandler(self, node):
if node.type is None:
self.errors.append(
B001(node.lineno, node.col_offset)
)


error = namedtuple('error', 'lineno col message type')
B001 = partial(
error,
message="B001: Do not use bare `except:`, it also catches unexpected "
"events like memory errors, interrupts, system exit, and so on. "
"Prefer `except Exception:`. If you're sure what you're doing, "
"be explicit and write `except BaseException:`.",
type=BugBearChecker,
)

57 changes: 57 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright (C) 2016 Łukasz Langa

import ast
import os
import re
from setuptools import setup


current_dir = os.path.abspath(os.path.dirname(__file__))
ld_file = open(os.path.join(current_dir, 'README.rst'))
try:
long_description = ld_file.read()
finally:
ld_file.close()


_version_re = re.compile(r'__version__\s+=\s+(?P<version>.*)')


with open('bugbear.py', 'rb') as f:
version = _version_re.search(f.read().decode('utf-8')).group('version')
version = str(ast.literal_eval(version))


setup(
name='flake8-bugbear',
version=version,
description="A plugin for flake8 finding likely bugs and design problems "
"in your program. Contains warnings that don't belong in "
"pyflakes and pep8.",
long_description=long_description,
keywords='flake8 bugbear bugs pyflakes pylint linter qa',
author='Łukasz Langa',
author_email='[email protected]',
url='https://github.com/ambv/flake8-bugbear',
license='MIT',
py_modules=['bugbear'],
zip_safe=False,
install_requires = ['flake8', 'attrs'],
test_suite='tests.test_bugbear',
classifiers=[
'Development Status :: 3 - Alpha',
'Environment :: Console',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3 :: Only',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Software Development :: Quality Assurance',
],
entry_points={
'flake8.extension': [
'B00 = bugbear:BugBearChecker',
],
},
)
Empty file added tests/__init__.py
Empty file.
49 changes: 49 additions & 0 deletions tests/b001.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Should emit:
B001 - on lines 8 and 40
"""

try:
import something
except:
# should be except ImportError:
import something_else as something

try:
pass
except ValueError:
# no warning here, all good
pass

try:
pass
except (KeyError, IndexError):
# no warning here, all good
pass

try:
pass
except BaseException as be:
# no warning here, all good
pass

try:
pass
except BaseException:
# no warning here, all good
pass


def func(**kwargs):
try:
is_debug = kwargs['debug']
except:
# should be except KeyError:
return

results = something.call(debug=is_debug)
try:
results['ok']
except: # noqa
# warning silenced
return
21 changes: 21 additions & 0 deletions tests/test_bugbear.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from pathlib import Path
import unittest

from bugbear import BugBearChecker, B001


class BugbearTestCase(unittest.TestCase):
maxDiff = None

def test_b001(self):
filename = Path(__file__).absolute().parent / 'b001.py'
bbc = BugBearChecker(filename=str(filename))
errors = list(bbc.run())
self.assertEqual(
errors,
[B001(8, 0), B001(40, 4)],
)


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

0 comments on commit 4980cc5

Please sign in to comment.