diff --git a/README.rst b/README.rst index 8c8c725..da27510 100644 --- a/README.rst +++ b/README.rst @@ -613,6 +613,8 @@ UNRELEASED ``map(print, items)`` on its own line never calls ``print`` (#452) * B909: also check mutations in ``if`` conditions and ``elif``/``else`` branches inside loops (#606) +* B042: don't treat a class named like an exception as one when its only base is + ``object``, as in ``class ParsedException(object)`` (#538) 26.9.30 ~~~~~~~ diff --git a/bugbear.py b/bugbear.py index 5b65b23..2afce72 100644 --- a/bugbear.py +++ b/bugbear.py @@ -3011,8 +3011,14 @@ def is_exception(s: str): # A class must inherit from a super class to be an exception, and we also # require the class name or any of the base names to look like an exception name. # A parameterized generic base such as `ContextManager[T]` does not count as - # that super class unless its own name looks like an exception name. - concrete_bases = [b for b in node.bases if not isinstance(b, ast.Subscript)] + # that super class unless its own name looks like an exception name, and + # neither does `object`, as in `class ParsedException(object)`. + concrete_bases = [ + b + for b in node.bases + if not isinstance(b, ast.Subscript) + and not (isinstance(b, ast.Name) and b.id == "object") + ] if not (is_exception(node.name) and concrete_bases): for base in node.bases: if isinstance(base, ast.Subscript): diff --git a/tests/eval_files/b042.py b/tests/eval_files/b042.py index bfd26d4..39eef13 100644 --- a/tests/eval_files/b042.py +++ b/tests/eval_files/b042.py @@ -96,6 +96,12 @@ def __init__(self, x): ... # safe class FooException: # safe, doesn't inherit from anything def __init__(self, x): ... +# `object` is not an exception base either +class ParsedException(object): # safe + def __init__(self, x): ... +class ObjectError(Anything, object): + def __init__(self, x): ... # B042: 4 + # a parameterized generic base is not an exception class unless its name says so class FailOnException(typing.ContextManager[int]): # safe def __init__(self, x): ...