From 8462c1ffe9d40845baeb575c4e598016e2cbc061 Mon Sep 17 00:00:00 2001 From: jakkdl Date: Thu, 29 Sep 2022 11:11:07 +0200 Subject: [PATCH] fixes #277, most of #278, and #280 --- bugbear.py | 19 +++++++++++++------ tests/b024.py | 25 +++++++++++++++++++++++-- tests/test_bugbear.py | 14 ++++++-------- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/bugbear.py b/bugbear.py index df4e268..c5ec2e4 100644 --- a/bugbear.py +++ b/bugbear.py @@ -616,13 +616,15 @@ def check_for_b024_and_b027(self, node: ast.ClassDef): """Check for inheritance from abstract classes in abc and lack of any methods decorated with abstract*""" - def is_abc_class(value): + def is_abc_class(value, name="ABC"): + # class foo(metaclass = [abc.]ABCMeta) if isinstance(value, ast.keyword): - return value.arg == "metaclass" and is_abc_class(value.value) - abc_names = ("ABC", "ABCMeta") - return (isinstance(value, ast.Name) and value.id in abc_names) or ( + return value.arg == "metaclass" and is_abc_class(value.value, "ABCMeta") + # class foo(ABC) + # class foo(abc.ABC) + return (isinstance(value, ast.Name) and value.id == name) or ( isinstance(value, ast.Attribute) - and value.attr in abc_names + and value.attr == name and isinstance(value.value, ast.Name) and value.value.id == "abc" ) @@ -647,6 +649,11 @@ def empty_body(body) -> bool: for stmt in body ) + # don't check multiple inheritance + # https://github.com/PyCQA/flake8-bugbear/issues/277 + if len(node.bases) + len(node.keywords) > 1: + return + # only check abstract classes if not any(map(is_abc_class, (*node.bases, *node.keywords))): return @@ -666,7 +673,7 @@ def empty_body(body) -> bool: if not has_abstract_decorator and empty_body(stmt.body): self.errors.append( - B025(stmt.lineno, stmt.col_offset, vars=(stmt.name,)) + B027(stmt.lineno, stmt.col_offset, vars=(stmt.name,)) ) if not has_abstract_method: diff --git a/tests/b024.py b/tests/b024.py index 936a1f5..7a11870 100644 --- a/tests/b024.py +++ b/tests/b024.py @@ -81,11 +81,32 @@ def method(self): foo() -class multi_super_1(notabc.ABC, abc.ABCMeta): # error +class multi_super_1(notabc.ABC, abc.ABCMeta): # safe def method(self): foo() -class multi_super_2(notabc.ABC, metaclass=abc.ABCMeta): # error +class multi_super_2(notabc.ABC, metaclass=abc.ABCMeta): # safe + def method(self): + foo() + + +class non_keyword_abcmeta_1(ABCMeta): # safe + def method(self): + foo() + + +class non_keyword_abcmeta_2(abc.ABCMeta): # safe + def method(self): + foo() + + +# very invalid code, but that's up to mypy et al to check +class keyword_abc_1(metaclass=ABC): # safe + def method(self): + foo() + + +class keyword_abc_2(metaclass=abc.ABC): # safe def method(self): foo() diff --git a/tests/test_bugbear.py b/tests/test_bugbear.py index 34f2d9e..e15c926 100644 --- a/tests/test_bugbear.py +++ b/tests/test_bugbear.py @@ -364,8 +364,6 @@ def test_b024(self): B024(58, 0, vars=("MetaBase_1",)), B024(69, 0, vars=("abc_Base_1",)), B024(74, 0, vars=("abc_Base_2",)), - B024(84, 0, vars=("multi_super_1",)), - B024(89, 0, vars=("multi_super_2",)), ) self.assertEqual(errors, expected) @@ -401,15 +399,15 @@ def test_b026(self): ) def test_b027(self): - filename = Path(__file__).absolute().parent / "b025.py" + filename = Path(__file__).absolute().parent / "b027.py" bbc = BugBearChecker(filename=str(filename)) errors = list(bbc.run()) expected = self.errors( - B025(13, 4, vars=("empty_1",)), - B025(16, 4, vars=("empty_2",)), - B025(19, 4, vars=("empty_3",)), - B025(23, 4, vars=("empty_4",)), - B025(31, 4, vars=("empty_5",)), + B027(13, 4, vars=("empty_1",)), + B027(16, 4, vars=("empty_2",)), + B027(19, 4, vars=("empty_3",)), + B027(23, 4, vars=("empty_4",)), + B027(31, 4, vars=("empty_5",)), ) self.assertEqual(errors, expected)