@@ -416,6 +416,7 @@ def visit_ClassDef(self, node):
416416 self .check_for_b903 (node )
417417 self .check_for_b018 (node )
418418 self .check_for_b021 (node )
419+ self .check_for_b024 (node )
419420 self .generic_visit (node )
420421
421422 def visit_Try (self , node ):
@@ -608,6 +609,37 @@ def check_for_b023(self, loop_node):
608609 if reassigned_in_loop .issuperset (err .vars ):
609610 self .errors .append (err )
610611
612+ def check_for_b024 (self , node : ast .ClassDef ):
613+ """Check for inheritance from abstract classes in abc and lack of
614+ any methods decorated with abstract*"""
615+
616+ def is_abc_class (value ):
617+ if isinstance (value , ast .keyword ):
618+ return value .arg == "metaclass" and is_abc_class (value .value )
619+ abc_names = ("ABC" , "ABCMeta" )
620+ return (isinstance (value , ast .Name ) and value .id in abc_names ) or (
621+ isinstance (value , ast .Attribute )
622+ and value .attr in abc_names
623+ and isinstance (value .value , ast .Name )
624+ and value .value .id == "abc"
625+ )
626+
627+ def is_abstract_decorator (expr ):
628+ return (isinstance (expr , ast .Name ) and expr .id [:8 ] == "abstract" ) or (
629+ isinstance (expr , ast .Attribute ) and expr .attr [:8 ] == "abstract"
630+ )
631+
632+ if not any (map (is_abc_class , (* node .bases , * node .keywords ))):
633+ return
634+
635+ for stmt in node .body :
636+ if isinstance (stmt , (ast .FunctionDef , ast .AsyncFunctionDef )) and any (
637+ map (is_abstract_decorator , stmt .decorator_list )
638+ ):
639+ return
640+
641+ self .errors .append (B024 (node .lineno , node .col_offset , vars = (node .name ,)))
642+
611643 def _get_assigned_names (self , loop_node ):
612644 loop_targets = (ast .For , ast .AsyncFor , ast .comprehension )
613645 for node in children_in_scope (loop_node ):
@@ -1139,6 +1171,12 @@ def visit_Lambda(self, node):
11391171)
11401172
11411173B023 = Error (message = "B023 Function definition does not bind loop variable {!r}." )
1174+ B024 = Error (
1175+ message = (
1176+ "{} is an abstract base class, but it has no abstract methods. Remember to use"
1177+ " @abstractmethod, @abstractclassmethod and/or @abstractproperty decorators."
1178+ )
1179+ )
11421180
11431181# Warnings disabled by default.
11441182B901 = Error (
0 commit comments