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)