diff --git a/README.rst b/README.rst index 5bc908d..a11ebc1 100644 --- a/README.rst +++ b/README.rst @@ -224,6 +224,12 @@ MIT Change Log ---------- +Unreleased +~~~~~~~~~~ + +* Update B014: ``binascii.Error`` is now treated as a subclass of ``ValueError`` + (#206) + 21.9.2 ~~~~~~~~~~ diff --git a/bugbear.py b/bugbear.py index 9211495..cbfdbbe 100644 --- a/bugbear.py +++ b/bugbear.py @@ -193,13 +193,12 @@ def visit_ExceptHandler(self, node): good = sorted(set(names), key=names.index) if "BaseException" in good: good = ["BaseException"] - # Find and remove aliases exceptions and only leave the primary alone - primaries = filter( - lambda primary: primary in good, B014.exception_aliases.keys() - ) - for primary in primaries: - aliases = B014.exception_aliases[primary] - good = list(filter(lambda e: e not in aliases, good)) + # Remove redundant exceptions that the automatic system either handles + # poorly (usually aliases) or can't be checked (e.g. it's not an + # built-in exception). + for primary, equivalents in B014.redundant_exceptions.items(): + if primary in good: + good = [g for g in good if g not in equivalents] for name, other in itertools.permutations(tuple(good), 2): if _typesafe_issubclass( @@ -758,15 +757,19 @@ def visit(self, node): "Write `except {2}{1}:`, which catches exactly the same exceptions." ) ) -B014.exception_aliases = { +B014.redundant_exceptions = { "OSError": { + # All of these are actually aliases of OSError since Python 3.3 "IOError", "EnvironmentError", "WindowsError", "mmap.error", "socket.error", "select.error", - } + }, + "ValueError": { + "binascii.Error", + }, } B015 = Error( message=( diff --git a/tests/b014.py b/tests/b014.py index 0144762..3847345 100644 --- a/tests/b014.py +++ b/tests/b014.py @@ -1,8 +1,9 @@ """ Should emit: -B014 - on lines 10, 16, 27, 41, 48, and 55 +B014 - on lines 11, 17, 28, 42, 49, 56, and 74. """ +import binascii import re try: @@ -66,3 +67,10 @@ class MyError(Exception): except (MyException, NotImplemented): # NotImplemented is not an exception, let's not crash on it. pass + + +try: + pass +except (ValueError, binascii.Error): + # binascii.Error is a subclass of ValueError. + pass diff --git a/tests/test_bugbear.py b/tests/test_bugbear.py index c9540a3..4cd01b9 100644 --- a/tests/test_bugbear.py +++ b/tests/test_bugbear.py @@ -179,16 +179,17 @@ def test_b014(self): bbc = BugBearChecker(filename=str(filename)) errors = list(bbc.run()) expected = self.errors( - B014(10, 0, vars=("Exception, TypeError", "", "Exception")), - B014(16, 0, vars=("OSError, OSError", " as err", "OSError")), - B014(27, 0, vars=("MyError, MyError", "", "MyError")), - B014(41, 0, vars=("MyError, BaseException", " as e", "BaseException")), - B014(48, 0, vars=("re.error, re.error", "", "re.error")), + B014(11, 0, vars=("Exception, TypeError", "", "Exception")), + B014(17, 0, vars=("OSError, OSError", " as err", "OSError")), + B014(28, 0, vars=("MyError, MyError", "", "MyError")), + B014(42, 0, vars=("MyError, BaseException", " as e", "BaseException")), + B014(49, 0, vars=("re.error, re.error", "", "re.error")), B014( - 55, + 56, 0, vars=("IOError, EnvironmentError, OSError", "", "OSError"), ), + B014(74, 0, vars=("ValueError, binascii.Error", "", "ValueError")), ) self.assertEqual(errors, expected)