-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🔧 MAINTAIN: Add
pylint_aiida
plugin (#5182)
As discussed in #5176 and pylint-dev/pylint#1694, pylint does not understand the `@classproperty` decorator, and so mistakes the method as a function, rather than an attribute of the class. This commit adds a pylint transform plugin, to remove false-positives. (see: https://pylint.pycqa.org/en/latest/how_tos/transform_plugins.html)
- Loading branch information
1 parent
8fb1457
commit bd91bdb
Showing
4 changed files
with
63 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# -*- coding: utf-8 -*- | ||
"""pylint plugin for ``aiida-core`` specific issues.""" | ||
import astroid | ||
|
||
|
||
def register(linter): # pylint: disable=unused-argument | ||
"""Register linters (unused)""" | ||
|
||
|
||
def remove_classprop_imports(import_from: astroid.ImportFrom): | ||
"""Remove any ``classproperty`` imports (handled in ``replace_classprops``)""" | ||
import_from.names = [name for name in import_from.names if name[0] != 'classproperty'] | ||
|
||
|
||
def replace_classprops(func: astroid.FunctionDef): | ||
"""Replace ``classproperty`` decorated methods. | ||
As discussed in https://github.com/PyCQA/pylint/issues/1694, pylint does not understand the ``@classproperty`` | ||
decorator, and so mistakes the method as a function, rather than an attribute of the class. | ||
If the method is annotated, this leads to pylint issuing ``no-member`` errors. | ||
This transform replaces ``classproperty`` decorated methods with an annotated attribute:: | ||
from aiida.common.lang import classproperty | ||
class MyClass: | ||
@classproperty | ||
def my_property(cls) -> AnnotatedType: | ||
return cls.my_value | ||
MyClass.my_property.attribute # <-- pylint issues: Method 'my_property' has no 'attribute' member (no-member) | ||
class MyClass: | ||
my_property: AnnotatedType | ||
""" | ||
# ignore methods without annotations or decorators | ||
if not (func.returns and func.decorators and func.decorators.nodes): | ||
return | ||
# ignore methods that are specified as abstract | ||
if any(isinstance(node, astroid.Name) and 'abstract' in node.name for node in func.decorators.nodes): | ||
return | ||
if any(isinstance(node, astroid.Attribute) and 'abstract' in node.attrname for node in func.decorators.nodes): | ||
return | ||
# convert methods with @classproperty decorator | ||
if isinstance(func.decorators.nodes[0], astroid.Name) and func.decorators.nodes[0].name == 'classproperty': | ||
assign = astroid.AnnAssign(lineno=func.lineno, col_offset=func.col_offset, parent=func.parent) | ||
assign.simple = 1 | ||
assign.target = astroid.AssignName(func.name, lineno=assign.lineno, col_offset=assign.col_offset, parent=assign) | ||
assign.annotation = func.returns | ||
assign.annotation.parent = assign | ||
func.parent.locals[func.name] = [assign.target] | ||
return assign | ||
|
||
|
||
astroid.MANAGER.register_transform(astroid.ImportFrom, remove_classprop_imports) | ||
astroid.MANAGER.register_transform(astroid.FunctionDef, replace_classprops) |