diff options
Diffstat (limited to 'pylint/checkers/classes.py')
-rw-r--r-- | pylint/checkers/classes.py | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 14e0ef635..489bb5063 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -79,6 +79,7 @@ from pylint.checkers.utils import ( safe_infer, unimplemented_abstract_methods, ) +from pylint.constants import PY38_PLUS from pylint.interfaces import IAstroidChecker from pylint.utils import get_global_option @@ -649,6 +650,16 @@ MSGS = { # pylint: disable=consider-using-namedtuple-or-dataclass "unused-private-member", "Emitted when a private member of a class is defined but not used.", ), + "W0239": ( + "Method %r overrides a method decorated with typing.final which is defined in class %r", + "overridden-final-method", + "Used when a method decorated with typing.final has been overridden.", + ), + "W0240": ( + "Class %r is a subclass of a class decorated with typing.final: %r", + "subclassed-final-class", + "Used when a class decorated with typing.final has been subclassed.", + ), "E0236": ( "Invalid object %r in __slots__, must contain only non empty strings", "invalid-slots-object", @@ -860,6 +871,7 @@ a metaclass class method.", self.add_message("no-init", args=node, node=node) self._check_slots(node) self._check_proper_bases(node) + self._check_typing_final(node) self._check_consistent_mro(node) def _check_consistent_mro(self, node): @@ -898,6 +910,23 @@ a metaclass class method.", "useless-object-inheritance", args=node.name, node=node ) + def _check_typing_final(self, node: nodes.ClassDef) -> None: + """Detect that a class does not subclass a class decorated with `typing.final`""" + if not PY38_PLUS: + return + for base in node.bases: + ancestor = safe_infer(base) + if not ancestor: + continue + if isinstance(ancestor, nodes.ClassDef) and decorated_with( + ancestor, ["typing.final"] + ): + self.add_message( + "subclassed-final-class", + args=(node.name, ancestor.name), + node=node, + ) + @check_messages("unused-private-member", "attribute-defined-outside-init") def leave_classdef(self, node: nodes.ClassDef) -> None: """close a class node: @@ -1347,6 +1376,12 @@ a metaclass class method.", args=(function_node.name, "non-async", "async"), node=function_node, ) + if decorated_with(parent_function_node, ["typing.final"]) and PY38_PLUS: + self.add_message( + "overridden-final-method", + args=(function_node.name, parent_function_node.parent.name), + node=function_node, + ) def _check_slots(self, node): if "__slots__" not in node.locals: |