Skip to content

Commit 5d9d744

Browse files
jakkdlcooperleesZac-HDpre-commit-ci[bot]
authored
B024: don't warn on classes without methods (#336)
* B024: don't warn on classes without methods. Also minor rephrasing of the error message, and changed the message in the README to match it better. * Update bugbear.py Yup, much clearer. Co-authored-by: Zac Hatfield-Dodds <zac.hatfield.dodds@gmail.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: Cooper Lees <me@cooperlees.com> Co-authored-by: Zac Hatfield-Dodds <zac.hatfield.dodds@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent e3fe79b commit 5d9d744

File tree

4 files changed

+20
-6
lines changed

4 files changed

+20
-6
lines changed

README.rst

+4-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ positives due to similarly named user-defined functions.
155155
the loop, because `late-binding closures are a classic gotcha
156156
<https://docs.python-guide.org/writing/gotchas/#late-binding-closures>`__.
157157

158-
**B024**: Abstract base class with no abstract method. You might have forgotten to add @abstractmethod.
158+
**B024**: Abstract base class has methods, but none of them are abstract. This
159+
is not necessarily an error, but you might have forgotten to add the @abstractmethod
160+
decorator, potentially in conjunction with @classmethod, @property and/or @staticmethod.
159161

160162
**B025**: ``try-except`` block with duplicate exceptions found.
161163
This check identifies exception types that are specified in multiple ``except``
@@ -312,6 +314,7 @@ Change Log
312314
Future
313315
~~~~~~~~~
314316

317+
* B024: now ignores classes without any methods.
315318
* B906: Ignore ``visit_`` functions with a ``_fields`` attribute that can't contain ast.AST subnodes. (#330)
316319
* B017: Don't warn when ``pytest.raises()`` has a ``match`` argument. (#334)
317320

bugbear.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,7 @@ def is_str_or_ellipsis(node):
721721
if not any(map(is_abc_class, (*node.bases, *node.keywords))):
722722
return
723723

724+
has_method = False
724725
has_abstract_method = False
725726

726727
for stmt in node.body:
@@ -733,6 +734,7 @@ def is_str_or_ellipsis(node):
733734
# only check function defs
734735
if not isinstance(stmt, (ast.FunctionDef, ast.AsyncFunctionDef)):
735736
continue
737+
has_method = True
736738

737739
has_abstract_decorator = any(
738740
map(is_abstract_decorator, stmt.decorator_list)
@@ -749,7 +751,7 @@ def is_str_or_ellipsis(node):
749751
B027(stmt.lineno, stmt.col_offset, vars=(stmt.name,))
750752
)
751753

752-
if not has_abstract_method:
754+
if has_method and not has_abstract_method:
753755
self.errors.append(B024(node.lineno, node.col_offset, vars=(node.name,)))
754756

755757
def check_for_b026(self, call: ast.Call):
@@ -1476,8 +1478,9 @@ def visit_Lambda(self, node):
14761478
B023 = Error(message="B023 Function definition does not bind loop variable {!r}.")
14771479
B024 = Error(
14781480
message=(
1479-
"B024 {} is an abstract base class, but it has no abstract methods. Remember to"
1480-
" use the @abstractmethod decorator, potentially in conjunction with"
1481+
"B024 {} is an abstract base class, but none of the methods it defines are"
1482+
" abstract. This is not necessarily an error, but you might have forgotten to"
1483+
" add the @abstractmethod decorator, potentially in conjunction with"
14811484
" @classmethod, @property and/or @staticmethod."
14821485
)
14831486
)

tests/b024.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,14 @@ class abc_set_class_variable_3(ABC): # safe
125125

126126

127127
# this doesn't actually declare a class variable, it's just an expression
128-
class abc_set_class_variable_4(ABC): # error
128+
# this is now filtered out by not having a method
129+
class abc_set_class_variable_4(ABC):
129130
foo
131+
132+
133+
class abc_class_no_method_1(ABC):
134+
pass
135+
136+
137+
class abc_class_no_method_2(ABC):
138+
foo()

tests/test_bugbear.py

-1
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,6 @@ def test_b024(self):
383383
B024(58, 0, vars=("MetaBase_1",)),
384384
B024(69, 0, vars=("abc_Base_1",)),
385385
B024(74, 0, vars=("abc_Base_2",)),
386-
B024(128, 0, vars=("abc_set_class_variable_4",)),
387386
)
388387
self.assertEqual(errors, expected)
389388

0 commit comments

Comments
 (0)