python-dev-tools
Advanced tools
+7
-1
| Metadata-Version: 2.1 | ||
| Name: python-dev-tools | ||
| Version: 2020.9.7 | ||
| Version: 2020.9.10 | ||
| Summary: Needed and up-to-date tools to develop in Python | ||
@@ -204,2 +204,8 @@ Home-page: https://github.com/vpoulailleau/python-dev-tools | ||
| 2020.9.10 | ||
| ^^^^^^^^^ | ||
| * The path provided to ``whatalinter`` can be the one of a directory | ||
| (recursive search of Python files) | ||
| 2020.9.7 | ||
@@ -206,0 +212,0 @@ ^^^^^^^^ |
+1
-1
| [tool.poetry] | ||
| name = "python_dev_tools" | ||
| version = "2020.9.7" | ||
| version = "2020.9.10" | ||
| description = "Needed and up-to-date tools to develop in Python" | ||
@@ -5,0 +5,0 @@ classifiers=[ |
| """Linter module, aggregation of linters.""" | ||
| import argparse | ||
| import contextlib | ||
| import io | ||
| import os | ||
| import pathlib | ||
| import re | ||
| import sys | ||
| from pathlib import Path | ||
| from typing import List, Optional, Union | ||
| from python_dev_tools.linters.common import DEFAULT_MESSAGE_FORMAT | ||
| from python_dev_tools.linters.lint import lint | ||
| from flake8.main import application | ||
| DEFAULT_MESSAGE_TEMPLATE = "%(path)s:%(row)d:%(col)d: %(code)s %(text)s" | ||
| def udpate_os_path(): | ||
| """Update PATH env variable to find linters.""" | ||
| script_path = pathlib.Path(__file__).resolve() | ||
| os.environ["PATH"] = "".join( | ||
| (str(script_path.parent), os.pathsep, os.environ["PATH"]), | ||
| path_var_name = "PATH" | ||
| script_path = Path(__file__).resolve() | ||
| os.environ[path_var_name] = "".join( | ||
| (str(script_path.parent), os.pathsep, os.environ[path_var_name]), | ||
| ) | ||
@@ -20,7 +27,100 @@ | ||
| if parent.stem == "lib": | ||
| os.environ["PATH"] = "".join( | ||
| (str(parent.parent / "bin"), os.pathsep, os.environ["PATH"]), | ||
| os.environ[path_var_name] = "".join( | ||
| (str(parent.parent / "bin"), os.pathsep, os.environ[path_var_name]), | ||
| ) | ||
| def _call_flake8(argv: Optional[List[str]] = None) -> None: | ||
| """Execute the main bit of the application. | ||
| This handles the creation of an instance of the class `Application`, runs it, | ||
| and then exits the application. | ||
| Args: | ||
| argv (Optional[List[str]]): The arguments to be passed to the application for | ||
| parsing. | ||
| """ | ||
| if argv is None: | ||
| argv = sys.argv[1:] | ||
| app = application.Application() | ||
| app.run(argv) | ||
| app.exit() | ||
| def _run_flake8(path: Path) -> str: | ||
| stdout = io.StringIO() | ||
| with contextlib.redirect_stdout(stdout): | ||
| try: | ||
| _call_flake8( | ||
| [ | ||
| str(path), | ||
| "--exit-zero", | ||
| "--max-line-length", | ||
| "88", | ||
| "--max-complexity", | ||
| "10", | ||
| "--inline-quotes", | ||
| '"', | ||
| # E203: space around : in slice | ||
| # WPS305: avoid f-strings | ||
| # WPS306: required explicit subclassing of object | ||
| # WPS602: avoid @staticmethod (can be subclassed…) | ||
| "--ignore=E203,WPS305,WPS306,WPS602", | ||
| ], | ||
| ) | ||
| except SystemExit: | ||
| pass # TODO what do we do here? | ||
| return stdout.getvalue() | ||
| def _filter_out(message: str) -> bool: | ||
| """Return True when message should be ignored. | ||
| Args: | ||
| message (str): message to analyze | ||
| Returns: | ||
| bool: True when message should be ignored, False otherwise | ||
| """ | ||
| for authorized_function in ("input", "print", "pprint"): | ||
| if f"Found wrong function call: {authorized_function}" in message: | ||
| return True | ||
| return False | ||
| def _add_info(message: str) -> str: | ||
| # TODO | ||
| return message | ||
| def _format(message: str, template: str) -> str: | ||
| regex = r"(?P<path>.*?):(?P<row>\d+):(?P<col>\d+):\s+(?P<code>.*?)\s+(?P<text>.*)" | ||
| match = re.match(regex, message) | ||
| if match: | ||
| infos = match.groupdict() | ||
| infos["row"] = int(infos["row"]) | ||
| infos["col"] = int(infos["col"]) | ||
| message = template % infos | ||
| else: | ||
| print("ERROR parsing:", message) | ||
| return message | ||
| def lint(path: Union[str, Path], template: str = DEFAULT_MESSAGE_TEMPLATE) -> None: | ||
| """Lint a file or a directory according to its path. | ||
| Args: | ||
| path (Union[str, Path]): path of the file or directory | ||
| template (str): template of linter message | ||
| """ | ||
| flake8_result = _run_flake8(path) | ||
| for message in flake8_result.splitlines(): | ||
| if _filter_out(message): | ||
| continue | ||
| message = _add_info(message) | ||
| print(_format(message, template)) | ||
| def main(): | ||
@@ -32,19 +132,17 @@ """Entry point.""" | ||
| parser.add_argument( | ||
| "file", metavar="FILE", type=str, help="path of the file to lint", | ||
| "path", | ||
| metavar="PATH", | ||
| type=str, | ||
| help="path of the file or directory to lint", | ||
| ) | ||
| parser.add_argument( | ||
| "-f", "--format", default=DEFAULT_MESSAGE_FORMAT, help="format of the output", | ||
| "-f", | ||
| "--format", | ||
| default=DEFAULT_MESSAGE_TEMPLATE, | ||
| help="format of the output", | ||
| ) | ||
| parser.add_argument( | ||
| "-a", | ||
| "--all", | ||
| action="store_true", | ||
| default=False, | ||
| help="display all warnings (default: display first ten warnings)", | ||
| ) | ||
| args = parser.parse_args() | ||
| udpate_os_path() | ||
| for message in lint(filepath=args.file, all_warnings=args.all): | ||
| print(message.formatted(args.format)) | ||
| lint(path=args.path, template=args.format) | ||
@@ -51,0 +149,0 @@ |
+6
-0
@@ -141,2 +141,8 @@ Python Dev Tools | ||
| 2020.9.10 | ||
| ^^^^^^^^^ | ||
| * The path provided to ``whatalinter`` can be the one of a directory | ||
| (recursive search of Python files) | ||
| 2020.9.7 | ||
@@ -143,0 +149,0 @@ ^^^^^^^^ |
+3
-3
@@ -5,3 +5,3 @@ # -*- coding: utf-8 -*- | ||
| packages = \ | ||
| ['python_dev_tools', 'python_dev_tools.formatters', 'python_dev_tools.linters'] | ||
| ['python_dev_tools', 'python_dev_tools.formatters'] | ||
@@ -58,5 +58,5 @@ package_data = \ | ||
| 'name': 'python-dev-tools', | ||
| 'version': '2020.9.7', | ||
| 'version': '2020.9.10', | ||
| 'description': 'Needed and up-to-date tools to develop in Python', | ||
| 'long_description': 'Python Dev Tools\n================\n\nNeeded and up-to-date tools to develop in Python (*WORK IN PROGRESS*)\n\n\n.. image:: https://img.shields.io/pypi/v/python_dev_tools.svg\n :target: https://pypi.python.org/pypi/python_dev_tools\n\n.. image:: https://img.shields.io/pypi/l/python_dev_tools.svg\n :target: https://github.com/vpoulailleau/python_dev_tools/blob/master/LICENSE\n\n.. image:: https://travis-ci.com/vpoulailleau/python-dev-tools.svg?branch=master\n :target: https://travis-ci.com/vpoulailleau/python-dev-tools\n\n.. image:: https://readthedocs.org/projects/python-dev-tools/badge/?version=latest\n :target: https://python-dev-tools.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n\n.. image:: https://pepy.tech/badge/python-dev-tools\n :target: https://pepy.tech/project/python-dev-tools\n :alt: Downloads\n\n.. image:: https://api.codeclimate.com/v1/badges/282fcd71714dabd6a847/test_coverage\n :target: https://codeclimate.com/github/vpoulailleau/python-dev-tools/test_coverage\n :alt: Test Coverage\n\n.. image:: https://api.codeclimate.com/v1/badges/282fcd71714dabd6a847/maintainability\n :target: https://codeclimate.com/github/vpoulailleau/python-dev-tools/maintainability\n :alt: Maintainability\n\n.. image:: https://bettercodehub.com/edge/badge/vpoulailleau/python-dev-tools?branch=master\n :target: https://bettercodehub.com/results/vpoulailleau/python-dev-tools\n :alt: Maintainability\n\n.. image:: https://img.shields.io/lgtm/grade/python/g/vpoulailleau/python-dev-tools.svg?logo=lgtm&logoWidth=1\n :target: https://lgtm.com/projects/g/vpoulailleau/python-dev-tools/context:python\n :alt: Maintainability\n\nDocumentation\n-------------\n\nThe full documentation can be read at https://python-dev-tools.readthedocs.io.\n\nInstallation\n------------\n\nIn a terminal, run:\n\n.. code-block:: console\n\n $ python3 -m pip install python-dev-tools --user --upgrade\n\nFull documentation on installation: https://python-dev-tools.readthedocs.io/en/latest/installation.html\n\nThat\'s it! Use the provided linter (``whatalinter``), formatter (``whataformatter``) and\nprecommit hook (TODO) where applicable.\n\nInstallation with Visual Studio Code\n------------------------------------\n\n* Follow the installation procedure for python-dev-tools\n* Be sure to have the official Python extension installed in VS Code\n* Open VS Code from within your activated virtual environment (in fact, make sure that \n ``whatalinter_vscode`` is in your ``PYTHON_PATH``)\n* In VS Code, open settings (F1 key, then type "Open Settings (JSON)",\n then enter)\n* Add in the opened JSON file (before the closing ``}``):\n\n.. code:: javascript\n\n "python.linting.enabled": true,\n "python.linting.flake8Enabled": true,\n "python.linting.flake8Path": "whatalinter_vscode",\n "python.formatting.provider": "black",\n "python.formatting.blackPath": "black",\n "python.formatting.blackArgs": [],\n\nFeatures\n--------\n\nIntegrate features of commonly used tools. This package provides usual\ndependencies to develop Python software.\n\n* Simple linter\n\n * ``whatalinter a_python_file.py`` lints a_python_file.py\n * output is compatible with the one of flake8 for easy integration in text editors\n and IDE\n * based on flake8 and plugins: https://gitlab.com/pycqa/flake8\n\n * darglint: https://github.com/terrencepreilly/darglint\n * flake8-2020: https://github.com/asottile/flake8-2020\n * flake8-bandit: https://github.com/tylerwince/flake8-bandit\n * flake8-broken-line: https://github.com/sobolevn/flake8-broken-line\n * flake8-bugbear: https://github.com/PyCQA/flake8-bugbear\n * flake8-builtins: https://github.com/gforcada/flake8-builtins\n * flake8-commas: https://github.com/PyCQA/flake8-commas/\n * flake8-comprehensions: https://github.com/adamchainz/flake8-comprehensions\n * flake8-debugger: https://github.com/JBKahn/flake8-debugger\n * flake8-docstrings: https://gitlab.com/pycqa/flake8-docstrings\n * flake8-eradicate: https://github.com/sobolevn/flake8-eradicate\n * flake8-fixme: https://github.com/tommilligan/flake8-fixme\n * flake8-isort: https://github.com/gforcada/flake8-isort\n * flake8-logging-format: https://github.com/globality-corp/flake8-logging-format\n * flake8-mutable: https://github.com/ebeweber/flake8-mutable\n * flake8-quotes: https://github.com/zheller/flake8-quotes/\n * flake8-rst-docstrings: https://github.com/peterjc/flake8-rst-docstrings\n * flake8-string-format: https://github.com/xZise/flake8-string-format\n * flake8-variables-names: https://github.com/best-doctor/flake8-variables-names\n * pep8-naming: https://github.com/PyCQA/pep8-naming\n * wemake-python-styleguide: https://github.com/wemake-services/wemake-python-styleguide\n\n* Simple formatter\n\n * ``whataformatter a_python_file.py`` formats a_python_file.py\n * based on\n\n * autoflake: https://github.com/myint/autoflake\n * black: https://github.com/python/black\n * pyupgrade: https://github.com/asottile/pyupgrade\n\n* Simple precommit hook\n\n * TODO\n\nLicense\n-------\n\nBSD 3-Clause license, feel free to contribute: https://python-dev-tools.readthedocs.io/en/latest/contributing.html.\n\nTODO\n----\n\n* documentation\n* precommit\n\nChangelog\n---------\n\n2020.9.7\n^^^^^^^^\n\n* Remove E203 in ``flake8`` for ``black`` compatibility\n\n2020.9.4\n^^^^^^^^\n\n* Add ``whatalinter_vscode`` for Visual Studio Code integration\n\n2020.9.2\n^^^^^^^^\n\n* Remove some warnings of ``wemake-python-styleguide``, for instance allow f-strings\n\n2020.9.1\n^^^^^^^^\n\n* Use ``poetry``\n* Remove redundant linters\n* Change max line length to 88 (default value of ``black``)\n* Replace ``pydocstyle`` with ``flake8-docstrings``\n* Add ``wemake-python-styleguide``\n\n2019.10.22\n^^^^^^^^^^\n\n* Add ``flake8-2020`` linter\n\n2019.07.21\n^^^^^^^^^^\n\n* Add ``--quiet`` and ``--diff`` flags to ``whataformatter`` for VS Code compatibility\n\n2019.07.20\n^^^^^^^^^^\n\n* Add ``black`` formatter\n* Add ``autoflake`` formatter\n* Add ``pyupgrade`` formatter\n\n2019.04.08\n^^^^^^^^^^\n\n* Add ``flake8`` linter\n* Add ``flake8-isort`` linter\n* Add ``pep8-naming`` linter\n* Add ``flake8-comprehensions`` linter\n* Add ``flake8-logging-format`` linter\n* Add ``flake8-bugbear`` linter\n* Add ``flake8-builtins`` linter\n* Add ``flake8-broken-line`` linter\n* Add ``flake8-fixme`` linter\n* Add ``flake8-mutable`` linter\n* Add ``flake8-debugger`` linter\n* Add ``flake8-variables-names`` linter\n* Add ``flake8-bandit`` linter\n\n2019.03.02\n^^^^^^^^^^\n\n* Add ``pydocstyle`` linter\n\n2019.03.01\n^^^^^^^^^^\n\n* Add McCabe complexity checker\n\n2019.02.26\n^^^^^^^^^^\n\n* Add ``pyflakes`` linter\n* Add ``pycodestyle`` linter\n\n2019.02.23\n^^^^^^^^^^\n\n* First release on PyPI.\n', | ||
| 'long_description': 'Python Dev Tools\n================\n\nNeeded and up-to-date tools to develop in Python (*WORK IN PROGRESS*)\n\n\n.. image:: https://img.shields.io/pypi/v/python_dev_tools.svg\n :target: https://pypi.python.org/pypi/python_dev_tools\n\n.. image:: https://img.shields.io/pypi/l/python_dev_tools.svg\n :target: https://github.com/vpoulailleau/python_dev_tools/blob/master/LICENSE\n\n.. image:: https://travis-ci.com/vpoulailleau/python-dev-tools.svg?branch=master\n :target: https://travis-ci.com/vpoulailleau/python-dev-tools\n\n.. image:: https://readthedocs.org/projects/python-dev-tools/badge/?version=latest\n :target: https://python-dev-tools.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n\n.. image:: https://pepy.tech/badge/python-dev-tools\n :target: https://pepy.tech/project/python-dev-tools\n :alt: Downloads\n\n.. image:: https://api.codeclimate.com/v1/badges/282fcd71714dabd6a847/test_coverage\n :target: https://codeclimate.com/github/vpoulailleau/python-dev-tools/test_coverage\n :alt: Test Coverage\n\n.. image:: https://api.codeclimate.com/v1/badges/282fcd71714dabd6a847/maintainability\n :target: https://codeclimate.com/github/vpoulailleau/python-dev-tools/maintainability\n :alt: Maintainability\n\n.. image:: https://bettercodehub.com/edge/badge/vpoulailleau/python-dev-tools?branch=master\n :target: https://bettercodehub.com/results/vpoulailleau/python-dev-tools\n :alt: Maintainability\n\n.. image:: https://img.shields.io/lgtm/grade/python/g/vpoulailleau/python-dev-tools.svg?logo=lgtm&logoWidth=1\n :target: https://lgtm.com/projects/g/vpoulailleau/python-dev-tools/context:python\n :alt: Maintainability\n\nDocumentation\n-------------\n\nThe full documentation can be read at https://python-dev-tools.readthedocs.io.\n\nInstallation\n------------\n\nIn a terminal, run:\n\n.. code-block:: console\n\n $ python3 -m pip install python-dev-tools --user --upgrade\n\nFull documentation on installation: https://python-dev-tools.readthedocs.io/en/latest/installation.html\n\nThat\'s it! Use the provided linter (``whatalinter``), formatter (``whataformatter``) and\nprecommit hook (TODO) where applicable.\n\nInstallation with Visual Studio Code\n------------------------------------\n\n* Follow the installation procedure for python-dev-tools\n* Be sure to have the official Python extension installed in VS Code\n* Open VS Code from within your activated virtual environment (in fact, make sure that \n ``whatalinter_vscode`` is in your ``PYTHON_PATH``)\n* In VS Code, open settings (F1 key, then type "Open Settings (JSON)",\n then enter)\n* Add in the opened JSON file (before the closing ``}``):\n\n.. code:: javascript\n\n "python.linting.enabled": true,\n "python.linting.flake8Enabled": true,\n "python.linting.flake8Path": "whatalinter_vscode",\n "python.formatting.provider": "black",\n "python.formatting.blackPath": "black",\n "python.formatting.blackArgs": [],\n\nFeatures\n--------\n\nIntegrate features of commonly used tools. This package provides usual\ndependencies to develop Python software.\n\n* Simple linter\n\n * ``whatalinter a_python_file.py`` lints a_python_file.py\n * output is compatible with the one of flake8 for easy integration in text editors\n and IDE\n * based on flake8 and plugins: https://gitlab.com/pycqa/flake8\n\n * darglint: https://github.com/terrencepreilly/darglint\n * flake8-2020: https://github.com/asottile/flake8-2020\n * flake8-bandit: https://github.com/tylerwince/flake8-bandit\n * flake8-broken-line: https://github.com/sobolevn/flake8-broken-line\n * flake8-bugbear: https://github.com/PyCQA/flake8-bugbear\n * flake8-builtins: https://github.com/gforcada/flake8-builtins\n * flake8-commas: https://github.com/PyCQA/flake8-commas/\n * flake8-comprehensions: https://github.com/adamchainz/flake8-comprehensions\n * flake8-debugger: https://github.com/JBKahn/flake8-debugger\n * flake8-docstrings: https://gitlab.com/pycqa/flake8-docstrings\n * flake8-eradicate: https://github.com/sobolevn/flake8-eradicate\n * flake8-fixme: https://github.com/tommilligan/flake8-fixme\n * flake8-isort: https://github.com/gforcada/flake8-isort\n * flake8-logging-format: https://github.com/globality-corp/flake8-logging-format\n * flake8-mutable: https://github.com/ebeweber/flake8-mutable\n * flake8-quotes: https://github.com/zheller/flake8-quotes/\n * flake8-rst-docstrings: https://github.com/peterjc/flake8-rst-docstrings\n * flake8-string-format: https://github.com/xZise/flake8-string-format\n * flake8-variables-names: https://github.com/best-doctor/flake8-variables-names\n * pep8-naming: https://github.com/PyCQA/pep8-naming\n * wemake-python-styleguide: https://github.com/wemake-services/wemake-python-styleguide\n\n* Simple formatter\n\n * ``whataformatter a_python_file.py`` formats a_python_file.py\n * based on\n\n * autoflake: https://github.com/myint/autoflake\n * black: https://github.com/python/black\n * pyupgrade: https://github.com/asottile/pyupgrade\n\n* Simple precommit hook\n\n * TODO\n\nLicense\n-------\n\nBSD 3-Clause license, feel free to contribute: https://python-dev-tools.readthedocs.io/en/latest/contributing.html.\n\nTODO\n----\n\n* documentation\n* precommit\n\nChangelog\n---------\n\n2020.9.10\n^^^^^^^^^\n\n* The path provided to ``whatalinter`` can be the one of a directory\n (recursive search of Python files)\n\n2020.9.7\n^^^^^^^^\n\n* Remove E203 in ``flake8`` for ``black`` compatibility\n\n2020.9.4\n^^^^^^^^\n\n* Add ``whatalinter_vscode`` for Visual Studio Code integration\n\n2020.9.2\n^^^^^^^^\n\n* Remove some warnings of ``wemake-python-styleguide``, for instance allow f-strings\n\n2020.9.1\n^^^^^^^^\n\n* Use ``poetry``\n* Remove redundant linters\n* Change max line length to 88 (default value of ``black``)\n* Replace ``pydocstyle`` with ``flake8-docstrings``\n* Add ``wemake-python-styleguide``\n\n2019.10.22\n^^^^^^^^^^\n\n* Add ``flake8-2020`` linter\n\n2019.07.21\n^^^^^^^^^^\n\n* Add ``--quiet`` and ``--diff`` flags to ``whataformatter`` for VS Code compatibility\n\n2019.07.20\n^^^^^^^^^^\n\n* Add ``black`` formatter\n* Add ``autoflake`` formatter\n* Add ``pyupgrade`` formatter\n\n2019.04.08\n^^^^^^^^^^\n\n* Add ``flake8`` linter\n* Add ``flake8-isort`` linter\n* Add ``pep8-naming`` linter\n* Add ``flake8-comprehensions`` linter\n* Add ``flake8-logging-format`` linter\n* Add ``flake8-bugbear`` linter\n* Add ``flake8-builtins`` linter\n* Add ``flake8-broken-line`` linter\n* Add ``flake8-fixme`` linter\n* Add ``flake8-mutable`` linter\n* Add ``flake8-debugger`` linter\n* Add ``flake8-variables-names`` linter\n* Add ``flake8-bandit`` linter\n\n2019.03.02\n^^^^^^^^^^\n\n* Add ``pydocstyle`` linter\n\n2019.03.01\n^^^^^^^^^^\n\n* Add McCabe complexity checker\n\n2019.02.26\n^^^^^^^^^^\n\n* Add ``pyflakes`` linter\n* Add ``pycodestyle`` linter\n\n2019.02.23\n^^^^^^^^^^\n\n* First release on PyPI.\n', | ||
| 'author': 'Vincent Poulailleau', | ||
@@ -63,0 +63,0 @@ 'author_email': 'vpoulailleau@gmail.com', |
| """Package.""" |
| """Common constants and class to all linters.""" | ||
| import re | ||
| import subprocess | ||
| from functools import total_ordering | ||
| DEFAULT_MESSAGE_FORMAT = "%(path)s:%(row)d:%(col)d: %(code)s %(text)s" | ||
| # TODO use dataclass | ||
| @total_ordering | ||
| class LinterMessage: | ||
| """Generic linter message.""" | ||
| def __init__( | ||
| self, | ||
| tool="unknown", | ||
| message_id="unknown", | ||
| filename="unknown", | ||
| lineno=1, | ||
| charno=1, | ||
| message="unknown", | ||
| extramessage="", | ||
| ): | ||
| """Initialize the new instance.""" | ||
| self.tool = tool | ||
| self.message_id = message_id | ||
| self.filename = filename | ||
| self.lineno = lineno | ||
| self.charno = charno | ||
| self.message = message | ||
| self.extramessage = extramessage | ||
| def __repr__(self): | ||
| """Represent as a string.""" | ||
| return self.formatted(DEFAULT_MESSAGE_FORMAT) | ||
| def as_tuple(self): | ||
| """Represent as a tuple for identification.""" | ||
| return ( | ||
| self.filename, | ||
| self.lineno, | ||
| self.charno, | ||
| self.tool, | ||
| self.message_id, | ||
| self.message, | ||
| self.message_id, | ||
| ) | ||
| def __lt__(self, other): | ||
| """Test less than.""" | ||
| return self.as_tuple() < other.as_tuple() | ||
| def __eq__(self, other): | ||
| """Test equality.""" | ||
| return self.as_tuple() == other.as_tuple() | ||
| def __hash__(self): | ||
| """Compute hash.""" | ||
| return hash(self.as_tuple()) | ||
| def formatted(self, format_string): | ||
| """Format the message according to format parameter.""" | ||
| data = { | ||
| "path": self.filename, | ||
| "row": self.lineno if self.lineno else 1, | ||
| "col": self.charno, | ||
| "code": f"{self.message_id}", | ||
| "text": f"[{self.tool}] {self.message}", | ||
| } | ||
| if self.extramessage: | ||
| data["text"] += f" ({self.extramessage})" | ||
| return format_string % data | ||
| class LinterNotFound(FileNotFoundError): | ||
| """ | ||
| Exception to detect that a linter is not found. | ||
| Note that this doesn't occur, except due to an installation error. | ||
| """ | ||
| class Linter: | ||
| """Base linter class.""" | ||
| name = "Linter" | ||
| path = "/bin/unknownlinter" | ||
| regex = [".*"] | ||
| @classmethod | ||
| def lint(cls, filepath): | ||
| """Execute the linter and return the list of messages.""" | ||
| try: | ||
| return cls._lint(filepath) | ||
| except LinterNotFound: | ||
| return [ | ||
| LinterMessage( | ||
| tool="whatalinter", | ||
| message_id="E999", | ||
| filename=str(filepath), | ||
| lineno=1, | ||
| charno=1, | ||
| message=f"linter not found: {cls.path}", | ||
| extramessage="", | ||
| ) | ||
| ] | ||
| @classmethod | ||
| def _lint(cls, filepath): | ||
| args = [cls.path, str(filepath)] | ||
| result = cls._execute_command(args) | ||
| return cls._parse_output(result.stdout) | ||
| @classmethod | ||
| def _execute_command(cls, args): | ||
| """Execute the linter or raise LinterNotFound.""" | ||
| try: | ||
| return subprocess.run( # noqa: S603 | ||
| args, | ||
| stdout=subprocess.PIPE, | ||
| stderr=subprocess.PIPE, | ||
| timeout=10, | ||
| encoding="utf-8", | ||
| ) | ||
| except FileNotFoundError as e: | ||
| if e.filename == cls.path: | ||
| raise LinterNotFound | ||
| else: | ||
| raise | ||
| @classmethod | ||
| def _parse_line(cls, line, regex, message=None, **kwargs): | ||
| match = re.match(regex, line) | ||
| if match: | ||
| if not message: | ||
| message = LinterMessage() | ||
| kwargs.update(match.groupdict()) | ||
| if "lineno" in kwargs: | ||
| kwargs["lineno"] = int(kwargs["lineno"]) | ||
| if "charno" in kwargs: | ||
| kwargs["charno"] = int(kwargs["charno"]) | ||
| for param, value in kwargs.items(): | ||
| setattr(message, param, value) | ||
| else: | ||
| print("ERROR parsing:", line) | ||
| return message | ||
| @classmethod | ||
| def _parse_output(cls, output): | ||
| messages = [] | ||
| message = None | ||
| regex_index = 0 | ||
| for line in output.splitlines(): | ||
| message = cls._parse_line( | ||
| line, cls.regex[regex_index], message, tool=cls.name, | ||
| ) | ||
| if isinstance(message, LinterMessage): | ||
| if regex_index == len(cls.regex) - 1: | ||
| regex_index = 0 | ||
| if not cls.filter_out(message): | ||
| messages.append(message) | ||
| message = None | ||
| else: | ||
| regex_index += 1 | ||
| return messages | ||
| @staticmethod | ||
| def filter_out(message: LinterMessage) -> bool: | ||
| """Return True when message should be ignored.""" | ||
| return False |
| """Flake8 linter management.""" | ||
| import contextlib | ||
| import io | ||
| from flake8.main.cli import main | ||
| from python_dev_tools.linters.common import Linter, LinterMessage | ||
| class Flake8Linter(Linter): | ||
| """Pycodestyle linter management.""" | ||
| name = "flake8" | ||
| path = "flake8" | ||
| regex = [ | ||
| ( | ||
| r"(?P<filename>.*?):(?P<lineno>\d+):(?P<charno>\d+):" | ||
| + r"\s+(?P<message_id>.*?)\s+(?P<message>.*)" | ||
| ), | ||
| ] | ||
| @classmethod | ||
| def _lint(cls, filepath): | ||
| stdout = io.StringIO() | ||
| with contextlib.redirect_stdout(stdout): | ||
| try: | ||
| main( | ||
| [ | ||
| str(filepath), | ||
| "--exit-zero", | ||
| "--max-line-length", | ||
| "88", | ||
| "--max-complexity", | ||
| "10", | ||
| "--inline-quotes", | ||
| '"', | ||
| # E203: space around : in slice | ||
| # WPS305: avoid f-strings | ||
| # WPS306: required explicit subclassing of object | ||
| # WPS602: avoid @staticmethod (can be subclassed…) | ||
| "--ignore=E203,WPS305,WPS306,WPS602", | ||
| ], | ||
| ) | ||
| except SystemExit: | ||
| # TODO what do we do here? | ||
| pass | ||
| return cls._parse_output(stdout.getvalue()) | ||
| @staticmethod | ||
| def filter_out(message: LinterMessage) -> bool: | ||
| """Return True when message should be ignored.""" | ||
| for authorized_function in ("input", "print", "pprint"): | ||
| if f"Found wrong function call: {authorized_function}" in message.message: | ||
| return True | ||
| return False |
| """Definition of lint function, calling all linters.""" | ||
| from python_dev_tools.linters.flake8 import Flake8Linter | ||
| linters = [ | ||
| Flake8Linter, | ||
| ] | ||
| def lint(filepath, all_warnings=False): | ||
| """Lint the file with known linters.""" | ||
| messages = set() | ||
| for linter in linters: | ||
| messages.update(linter.lint(filepath)) | ||
| if len(messages) >= 10 and not all_warnings: | ||
| break | ||
| messages = sorted(messages) | ||
| if all_warnings: | ||
| return messages | ||
| return messages[:10] |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
39692
-9.47%15
-21.05%377
-26.22%