From 2444a98580af8bd9d66be32a0518f074ce1561c5 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Wed, 20 Oct 2021 11:20:44 +0200 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=94=A7=20MAINTAIN:=20Move=20pylint=20?= =?UTF-8?q?plugin=20into=20aiida?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will allow pylint/pre-commit to be called from anywhere, and also allows for plugins to use it if necessary. --- utils/pylint_aiida.py => aiida/tools/pylint_plugin.py | 8 +++++++- pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) rename utils/pylint_aiida.py => aiida/tools/pylint_plugin.py (92%) diff --git a/utils/pylint_aiida.py b/aiida/tools/pylint_plugin.py similarity index 92% rename from utils/pylint_aiida.py rename to aiida/tools/pylint_plugin.py index f577057b7b..87503b7672 100644 --- a/utils/pylint_aiida.py +++ b/aiida/tools/pylint_plugin.py @@ -1,5 +1,11 @@ # -*- coding: utf-8 -*- -"""pylint plugin for ``aiida-core`` specific issues.""" +"""pylint plugin for ``aiida-core`` specific linting issues. + +the plugin can be loaded into pylint in the pyproject.toml:: + + [tool.pylint.master] + load-plugins = ["aiida.tools.pylint_plugin"] +""" import astroid diff --git a/pyproject.toml b/pyproject.toml index dae3953e5a..c648798b24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=40.8.0", "wheel", "fastentrypoints~=0.12"] build-backend = "setuptools.build_meta" [tool.pylint.master] -load-plugins = ["pylint_django", "utils.pylint_aiida"] +load-plugins = ["pylint_django", "aiida.tools.pylint_plugin"] # this currently fails with aiida.common.exceptions.ProfileConfigurationError: no profile has been loaded # we woud need a static settings module to use this # django-settings-module = "aiida.backends.djsite.settings" From 570efe74821b81be6698df6ac93bc5dfb0285e22 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Wed, 20 Oct 2021 11:48:45 +0200 Subject: [PATCH 2/4] fix docs --- aiida/tools/pylint_plugin.py | 15 ++++++++++----- docs/source/nitpick-exceptions | 3 +++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/aiida/tools/pylint_plugin.py b/aiida/tools/pylint_plugin.py index 87503b7672..4ad8b72986 100644 --- a/aiida/tools/pylint_plugin.py +++ b/aiida/tools/pylint_plugin.py @@ -6,19 +6,23 @@ [tool.pylint.master] load-plugins = ["aiida.tools.pylint_plugin"] """ -import astroid +try: + import astroid + INSTALLED = True +except ImportError: + INSTALLED = False def register(linter): # pylint: disable=unused-argument """Register linters (unused)""" -def remove_classprop_imports(import_from: astroid.ImportFrom): +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): +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`` @@ -59,5 +63,6 @@ class MyClass: return assign -astroid.MANAGER.register_transform(astroid.ImportFrom, remove_classprop_imports) -astroid.MANAGER.register_transform(astroid.FunctionDef, replace_classprops) +if INSTALLED: + astroid.MANAGER.register_transform(astroid.ImportFrom, remove_classprop_imports) + astroid.MANAGER.register_transform(astroid.FunctionDef, replace_classprops) diff --git a/docs/source/nitpick-exceptions b/docs/source/nitpick-exceptions index d7bfb15798..2aa08beebf 100644 --- a/docs/source/nitpick-exceptions +++ b/docs/source/nitpick-exceptions @@ -109,6 +109,9 @@ py:class plumpy.workchains._While # Note: These exceptions are needed if # * the objects are referenced e.g. as param/return types types in method docstrings (without intersphinx mapping) # * the documentation linked via intersphinx lists the objects at a different (usually lower) import hierarchy +py:class astroid.ImportFrom +py:class astroid.FunctionDef + py:class click.core.Group py:class click.core.Option py:class click.types.ParamType From 7f7e939140fc73af413e097a16922f7f4275b899 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Tue, 26 Oct 2021 11:31:12 +0200 Subject: [PATCH 3/4] Moved to https://github.com/aiidateam/pylint-aiida --- aiida/tools/pylint_plugin.py | 68 ------------------------------------ pyproject.toml | 2 +- setup.json | 1 + 3 files changed, 2 insertions(+), 69 deletions(-) delete mode 100644 aiida/tools/pylint_plugin.py diff --git a/aiida/tools/pylint_plugin.py b/aiida/tools/pylint_plugin.py deleted file mode 100644 index 4ad8b72986..0000000000 --- a/aiida/tools/pylint_plugin.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- coding: utf-8 -*- -"""pylint plugin for ``aiida-core`` specific linting issues. - -the plugin can be loaded into pylint in the pyproject.toml:: - - [tool.pylint.master] - load-plugins = ["aiida.tools.pylint_plugin"] -""" -try: - import astroid - INSTALLED = True -except ImportError: - INSTALLED = False - - -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 - - -if INSTALLED: - astroid.MANAGER.register_transform(astroid.ImportFrom, remove_classprop_imports) - astroid.MANAGER.register_transform(astroid.FunctionDef, replace_classprops) diff --git a/pyproject.toml b/pyproject.toml index c648798b24..7f617b0bc9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=40.8.0", "wheel", "fastentrypoints~=0.12"] build-backend = "setuptools.build_meta" [tool.pylint.master] -load-plugins = ["pylint_django", "aiida.tools.pylint_plugin"] +load-plugins = ["pylint_aiida", "pylint_django"] # this currently fails with aiida.common.exceptions.ProfileConfigurationError: no profile has been loaded # we woud need a static settings module to use this # django-settings-module = "aiida.backends.djsite.settings" diff --git a/setup.json b/setup.json index 8fe08cb42a..af46421000 100644 --- a/setup.json +++ b/setup.json @@ -98,6 +98,7 @@ "packaging==20.3", "pre-commit~=2.2", "pylint~=2.11.1", + "pylint-aiida~=0.1.1", "pylint-django", "sqlalchemy2-stubs" ], From 687049f4a310c968095797f429774a4a1d8a7426 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Tue, 26 Oct 2021 11:50:38 +0200 Subject: [PATCH 4/4] Update nitpick-exceptions --- docs/source/nitpick-exceptions | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/source/nitpick-exceptions b/docs/source/nitpick-exceptions index 2aa08beebf..d7bfb15798 100644 --- a/docs/source/nitpick-exceptions +++ b/docs/source/nitpick-exceptions @@ -109,9 +109,6 @@ py:class plumpy.workchains._While # Note: These exceptions are needed if # * the objects are referenced e.g. as param/return types types in method docstrings (without intersphinx mapping) # * the documentation linked via intersphinx lists the objects at a different (usually lower) import hierarchy -py:class astroid.ImportFrom -py:class astroid.FunctionDef - py:class click.core.Group py:class click.core.Option py:class click.types.ParamType