diff options
author | Laura M?dioni <laura.medioni@logilab.fr> | 2015-10-29 09:24:52 +0100 |
---|---|---|
committer | Laura M?dioni <laura.medioni@logilab.fr> | 2015-10-29 09:24:52 +0100 |
commit | e2da1f9f3dbd9a6462e0549da75b37249467afb8 (patch) | |
tree | 94ec39fb735cc02832fe5853460af07b0ea13b30 /pylint/checkers/classes.py | |
parent | 486091452ad54d6bac7cc0100c62652e858aa8c8 (diff) | |
download | pylint-e2da1f9f3dbd9a6462e0549da75b37249467afb8.tar.gz |
check for class methods declared without a decorator
related to the issue #675
Diffstat (limited to 'pylint/checkers/classes.py')
-rw-r--r-- | pylint/checkers/classes.py | 39 |
1 files changed, 36 insertions, 3 deletions
diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 86ccb19..98a13bf 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -262,7 +262,10 @@ MSGS = { 'E0241': ('Duplicate bases for class %r', 'duplicate-bases', 'Used when a class has duplicate bases.'), - + 'R0202': ('Consider using a decorator instead of calling classmethod', + 'no-classmethod-decorator', + 'Used when a class method is defined without using the decorator ' + 'syntax.'), } @@ -613,17 +616,47 @@ a metaclass class method.'} self.add_message('assigning-non-slot', args=(node.attrname, ), node=node) - @check_messages('protected-access') + @check_messages('protected-access', 'no-classmethod-decorator') def visit_assign(self, assign_node): + self._check_classmethod_declaration(assign_node) node = assign_node.targets[0] if not isinstance(node, astroid.AssignAttr): return if self.is_first_attr(node): return - self._check_protected_attribute_access(node) + def _check_classmethod_declaration(self, node): + """checks for uses of classmethod() instead of @classmethod decorator + + A message will be emitted only if the assignment is at a class scope + and only if the classmethod's argument belongs to the class where it + is defined. + `node` is an assign node. + """ + if not isinstance(node.value, astroid.Call): + return + # check the function called is "classmethod" + func = node.value.func + if not isinstance(func, astroid.Name) or not func.name == 'classmethod': + return + # assignment must be at a class scope + parent_class = node.parent + if not isinstance(parent_class, astroid.ClassDef): + return + # Check if the arg passed to classmethod is a class member + classmeth_arg = node.value.args[0] + if not isinstance(classmeth_arg, astroid.Name): + return + method_name = classmeth_arg.name + for member in parent_class.get_children(): + if (isinstance(member, astroid.FunctionDef) and + method_name == member.name): + self.add_message('no-classmethod-decorator', + node=node.targets[0]) + break + def _check_protected_attribute_access(self, node): '''Given an attribute access node (set or get), check if attribute access is legitimate. Call _check_first_attr with node before calling |