Source code for deal._cli._lint

from __future__ import annotations

import ast
import json
from argparse import ArgumentParser
from pathlib import Path
from textwrap import dedent, indent
from typing import Iterable, Iterator

from .._colors import get_colors, highlight
from ..linter import Checker
from ._base import Command
from ._common import get_paths


TEMPLATE = '  {blue}{row}{end}:{blue}{col}{end} {magenta}{code}{end} {yellow}{text}{end}'
VALUE = ' {magenta}({value}){end}'
POINTER = '{magenta}^{end}'


[docs]class LintCommand(Command): """Run linter against the given files. ```bash python3 -m deal lint project/ ``` Options: + `--json`: output violations as [json per line][ndjson]. + `--nocolor`: output violations in human-friendly format but without colors. Useful for running linter on CI. Exit code is equal to the found violations count. See [linter][linter] documentation for more details. [ndjson]: http://ndjson.org/ [linter]: https://deal.readthedocs.io/basic/linter.html """ @staticmethod def init_parser(parser: ArgumentParser) -> None: parser.add_argument('--json', action='store_true', help='json output') parser.add_argument('--nocolor', action='store_true', help='colorless output') parser.add_argument('paths', nargs='*', default='.') def __call__(self, args) -> int: prev = None errors = list(self.get_errors(paths=args.paths)) colors = get_colors(args) for error in errors: if args.json: self.print(json.dumps(error)) continue # print file path if error['path'] != prev: self.print('{green}{path}{end}'.format(**colors, **error)) prev = error['path'] # print message line = TEMPLATE.format(**colors, **error) if error['value']: line += VALUE.format(**colors, **error) self.print(line) # print code line pointer = ' ' * error['col'] + POINTER.format(**colors) content = error['content'] if not args.nocolor: content = highlight(content) content += '\n' + pointer content = indent(dedent(content), prefix=' ') self.print(content) return len(errors) @staticmethod def get_errors(paths: Iterable[str]) -> Iterator[dict]: for arg in paths: for path in get_paths(Path(arg)): content = path.read_text() checker = Checker( filename=str(path), tree=ast.parse(content), ) lines = content.split('\n') for error in checker.get_errors(): yield dict( path=str(path), row=error.row, col=error.col, code=error.full_code, text=error.text, value=error.value, content=lines[error.row - 1], )