From: Daniel Wagner Date: Thu, 3 Mar 2022 17:31:44 +0000 (+0100) Subject: tests: Add support for linting and formatting of Python code X-Git-Tag: v2.0-rc5~1^2~1 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=ed94470b5f2c67767a04b23b5cce580d11f10c64;p=users%2Fsagi%2Fnvme-cli.git tests: Add support for linting and formatting of Python code Shamelessly steal the linting and formatting tooling for Python code form python-sdbus. Update the documentation accordingly. Link: https://github.com/python-sdbus/python-sdbus Signed-off-by: Daniel Wagner --- diff --git a/tests/README b/tests/README index 686bd045..53ffc592 100644 --- a/tests/README +++ b/tests/README @@ -11,18 +11,16 @@ nvmetests 1. Common Package Dependencies ------------------------------ - 1. Python(>= 2.7.5 or >= 3.3) - 2. nose(http://nose.readthedocs.io/en/latest/) - 3. nose2(Installation guide http://nose2.readthedocs.io/) - 4. pep8(https://pypi.python.org/pypi/setuptools-pep8) - 5. flake8(https://pypi.python.org/pypi/flake8) - 6. pylint(https://www.pylint.org/) - 7. Epydoc(http://epydoc.sourceforge.net/) - 8. nvme-cli(https://github.com/linux-nvme/nvme-cli.git) + 1. Python(>= 3.3) + 2. nose2 (Installation guide http://nose2.readthedocs.io/) + 3. flake8 (https://pypi.python.org/pypi/flake8) + 4. mypy (https://pypi.org/project/mypy/) + 5. autopep8 (https://pypi.org/project/autopep8/) + 6. isort (https://pypi.org/project/isort/) Python package management system pip can be used to install most of the listed packages(https://pip.pypa.io/en/stable/installing/) :- - $ pip install nose nose2 pep8 flake8 pylint epydoc + $ pip install nose2 flake8 mypy autopep8 isort 2. Overview ----------- @@ -76,12 +74,12 @@ nvmetests 6. Before writing a new function have a look into TestNVMe to see if it can be reused. 7. Once testcase is ready make sure :- - a. Run pep8, flake8, pylint on the testcase and fix errors/warnings. - -Example "$ make static_check" will run pep8, flake8 and pylint on - all the python files in current directory. - b. Execute make doc to generate the documentation. - -Example "$ make doc" will create and update existing - documentation. + a. Run flake8, mypy, autopep8 and isort on the testcase and fix + errors/warnings. + - Example "$ ninja -C .build lint-python" will run flake8 and + mypy on all the python files in current directory. + - Example "$ ninja -C .build fomrat-python" will run autopep8 and + isort on all the python files in the current directory. 4. Running testcases with framework ----------------------------------- @@ -89,5 +87,5 @@ nvmetests $ nose2 --verbose nvme_writezeros_test $ nose2 --verbose nvme_read_write_test - 2. Running all the testcases with Makefile :- - $ make run + 2. Running all the testcases with ninja :- + $ ninja test -C .build diff --git a/tests/meson.build b/tests/meson.build index 5acf2aaf..efd3ea8f 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -30,3 +30,45 @@ if runtests.found() timeout: 500) endforeach endif + +python_module = import('python') + +python = python_module.find_installation('python3') + +mypy = find_program( + 'mypy', + required : false, +) +flake8 = find_program( + 'flake8', + required : false, +) +linter_script = files('run_py_linters.py') + +if mypy.found() and flake8.found() + run_target( + 'lint-python', + command : [python, linter_script, 'lint'], + ) +else + message('Mypy or Flake8 not found. Python linting disabled') +endif + + +autopep8 = find_program( + 'autopep8', + required : false, +) +isort = find_program( + 'isort', + required : false, +) + +if autopep8.found() and isort.found() + run_target( + 'format-python', + command : [python, linter_script, 'format'], + ) +else + message('autopep8 or isort not found. Python formating disabled') +endif diff --git a/tests/run_py_linters.py b/tests/run_py_linters.py new file mode 100644 index 00000000..869b3e46 --- /dev/null +++ b/tests/run_py_linters.py @@ -0,0 +1,123 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +# Copied from https://github.com/python-sdbus/python-sdbus +# Copyright (C) 2020, 2021 igo95862 + +# This file is part of nvme-cli + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +from __future__ import annotations + +from argparse import ArgumentParser +from os import environ +from pathlib import Path +from subprocess import run +from typing import List + +source_root = Path(environ['MESON_SOURCE_ROOT']) +build_dir = Path(environ['MESON_BUILD_ROOT']) + +tests_dir = source_root / 'tests' + +all_python_modules = [ + tests_dir, +] + +mypy_cache_dir = build_dir / '.mypy_cache' + + +def run_mypy(path: Path) -> None: + print(f"Running mypy on {path}") + run( + args=( + 'mypy', '--strict', + '--cache-dir', mypy_cache_dir, + '--python-version', '3.8', + '--namespace-packages', + '--ignore-missing-imports', + path, + ), + check=False, + env={'MYPYPATH': str(tests_dir.absolute()), **environ}, + ) + + +def linter_main() -> None: + run( + args=( + 'flake8', + *all_python_modules, + ), + check=False, + ) + + for x in all_python_modules: + run_mypy(x) + + +def get_all_python_files() -> List[Path]: + python_files: List[Path] = [] + + for python_module in all_python_modules: + if python_module.is_dir(): + for a_file in python_module.iterdir(): + if a_file.suffix == '.py': + python_files.append(a_file) + else: + python_files.append(python_module) + + return python_files + + +def formater_main() -> None: + all_python_files = get_all_python_files() + + run( + args=('autopep8', '--in-place', *all_python_files), + check=False, + ) + + run( + args=( + 'isort', + '-m', 'VERTICAL_HANGING_INDENT', + '--trailing-comma', + *all_python_files, + ), + check=False, + ) + + +def main() -> None: + parser = ArgumentParser() + parser.add_argument( + 'mode', + choices=('lint', 'format'), + ) + + args = parser.parse_args() + + mode = args.mode + + if mode == 'lint': + linter_main() + elif mode == 'format': + formater_main() + else: + raise ValueError('Unknown mode', mode) + + +if __name__ == '__main__': + main()