Skip to content

Commit

Permalink
feat: add colours to text output (#368)
Browse files Browse the repository at this point in the history
* feat(violations): quote dependency name

* feat(cli): add an option to disable ANSI output

* feat: pass `no_ansi` to `TextReporter`

* feat(reporters): add color to text output

* refactor(location): remove obsolete `format_for_terminal`

* chore(deps): add `colorama` on Windows

* feat: instantiate colorama on Windows

* docs(usage): document `--no-ansi`
  • Loading branch information
mkniewallner authored May 8, 2023
1 parent 3e7a258 commit e3608e5
Show file tree
Hide file tree
Showing 22 changed files with 345 additions and 120 deletions.
13 changes: 13 additions & 0 deletions deptry/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import logging
import sys
from collections import defaultdict
from importlib.metadata import version
from pathlib import Path
Expand All @@ -14,6 +15,11 @@
if TYPE_CHECKING:
from collections.abc import Mapping, Sequence

if sys.platform == "win32":
from colorama import just_fix_windows_console

just_fix_windows_console()

DEFAULT_EXCLUDE = ("venv", r"\.venv", r"\.direnv", "tests", r"\.git", r"setup\.py")


Expand Down Expand Up @@ -119,6 +125,11 @@ def display_deptry_version(ctx: click.Context, _param: click.Parameter, value: b
help="Path to the pyproject.toml file to read configuration from.",
default="pyproject.toml",
)
@click.option(
"--no-ansi",
is_flag=True,
help="Disable ANSI characters in terminal output.",
)
@click.option(
"--skip-obsolete",
is_flag=True,
Expand Down Expand Up @@ -259,6 +270,7 @@ def display_deptry_version(ctx: click.Context, _param: click.Parameter, value: b
def deptry(
root: Path,
config: Path,
no_ansi: bool,
ignore_obsolete: tuple[str, ...],
ignore_missing: tuple[str, ...],
ignore_transitive: tuple[str, ...],
Expand Down Expand Up @@ -286,6 +298,7 @@ def deptry(
Core(
root=root,
config=config,
no_ansi=no_ansi,
ignore_obsolete=ignore_obsolete,
ignore_missing=ignore_missing,
ignore_transitive=ignore_transitive,
Expand Down
3 changes: 2 additions & 1 deletion deptry/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
class Core:
root: Path
config: Path
no_ansi: bool
ignore_obsolete: tuple[str, ...]
ignore_missing: tuple[str, ...]
ignore_transitive: tuple[str, ...]
Expand Down Expand Up @@ -88,7 +89,7 @@ def run(self) -> None:
]

violations = self._find_violations(imported_modules_with_locations, dependencies_extract.dependencies)
TextReporter(violations).report()
TextReporter(violations, use_ansi=not self.no_ansi).report()

if self.json_output:
JSONReporter(violations, self.json_output).report()
Expand Down
5 changes: 0 additions & 5 deletions deptry/imports/location.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,3 @@ class Location:
file: Path
line: int | None = None
column: int | None = None

def format_for_terminal(self) -> str:
if self.line is not None and self.column is not None:
return f"{self.file}:{self.line}:{self.column}"
return str(self.file)
54 changes: 46 additions & 8 deletions deptry/reporters/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,29 @@

import logging
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any

from deptry.reporters.base import Reporter

if TYPE_CHECKING:
from deptry.imports.location import Location
from deptry.violations import Violation


COLORS = {
"BOLD": "\033[1m",
"CYAN": "\033[36m",
"GREEN": "\033[32m",
"RED": "\033[31m",
"RESET": "\033[m",
}
COLORS_NOOP = {color: "" for color in COLORS}


@dataclass
class TextReporter(Reporter):
use_ansi: bool = True

def report(self) -> None:
self._log_and_exit()

Expand All @@ -20,20 +33,45 @@ def _log_and_exit(self) -> None:

self._log_total_number_of_violations_found(self.violations)

@staticmethod
def _log_total_number_of_violations_found(violations: list[Violation]) -> None:
def _log_total_number_of_violations_found(self, violations: list[Violation]) -> None:
if violations:
logging.info(f"Found {len(violations)} dependency {'issues' if len(violations) > 1 else 'issue'}.")
logging.info(
self._stylize(
"{BOLD}{RED}Found {total} dependency {issue_word}.{RESET}",
total=len(violations),
issue_word="issues" if len(violations) > 1 else "issue",
)
)
logging.info("\nFor more information, see the documentation: https://fpgmaas.github.io/deptry/")
else:
logging.info("Success! No dependency issues found.")
logging.info(self._stylize("{BOLD}{GREEN}Success! No dependency issues found.{RESET}"))

def _log_violations(self, violations: list[Violation]) -> None:
logging.info("")

for violation in violations:
logging.info(self._format_error(violation))

@classmethod
def _format_error(cls, violation: Violation) -> str:
return f"{(violation.location.format_for_terminal())}: {violation.error_code} {violation.get_error_message()}"
def _format_error(self, violation: Violation) -> str:
return self._stylize(
"{location}{CYAN}:{RESET} {BOLD}{RED}{error_code}{RESET} {error_message}",
location=self._format_location(violation.location),
error_code=violation.error_code,
error_message=violation.get_error_message(),
)

def _format_location(self, location: Location) -> str:
if location.line is not None and location.column is not None:
return self._stylize(
"{BOLD}{file}{RESET}{CYAN}:{RESET}{line}{CYAN}:{RESET}{column}",
file=location.file,
line=location.line,
column=location.column,
)
return self._stylize("{BOLD}{file}{RESET}", file=location.file)

def _stylize(self, text: str, **kwargs: Any) -> str:
return text.format(**kwargs, **self._get_colors())

def _get_colors(self) -> dict[str, str]:
return COLORS if self.use_ansi else COLORS_NOOP
2 changes: 1 addition & 1 deletion deptry/violations/misplaced_dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@dataclass
class MisplacedDevDependencyViolation(Violation):
error_code: ClassVar[str] = "DEP004"
error_template: ClassVar[str] = "{name} imported but declared as a dev dependency"
error_template: ClassVar[str] = "'{name}' imported but declared as a dev dependency"
issue: Module

def get_error_message(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion deptry/violations/missing.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@dataclass
class MissingDependencyViolation(Violation):
error_code: ClassVar[str] = "DEP001"
error_template: ClassVar[str] = "{name} imported but missing from the dependency definitions"
error_template: ClassVar[str] = "'{name}' imported but missing from the dependency definitions"
issue: Module

def get_error_message(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion deptry/violations/obsolete.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@dataclass
class ObsoleteDependencyViolation(Violation):
error_code: ClassVar[str] = "DEP002"
error_template: ClassVar[str] = "{name} defined as a dependency but not used in the codebase"
error_template: ClassVar[str] = "'{name}' defined as a dependency but not used in the codebase"
issue: Dependency

def get_error_message(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion deptry/violations/transitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@dataclass
class TransitiveDependencyViolation(Violation):
error_code: ClassVar[str] = "DEP003"
error_template: ClassVar[str] = "{name} imported but it is a transitive dependency"
error_template: ClassVar[str] = "'{name}' imported but it is a transitive dependency"
issue: Module

def get_error_message(self) -> str:
Expand Down
12 changes: 12 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ Path to the `pyproject.toml` file that holds _deptry_'s configuration and depend
deptry . --config sub_directory/pyproject.toml
```

#### No ANSI

Disable ANSI characters in terminal output.

- Type: `bool`
- Default: `False`
- CLI option name: `--no-ansi`
- CLI example:
```shell
deptry . --no-ansi
```

#### Exclude

List of patterns to exclude when searching for source files.
Expand Down
14 changes: 13 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ classifiers = [
python = ">=3.8,<4.0"
chardet = ">=4.0.0"
click = "^8.0.0"
colorama = { version = ">=0.4.6", markers = "sys_platform == 'win32'" }
pathspec = ">=0.9.0"
tomli = { version = "^2.0.1", python = "<3.11" }

Expand All @@ -34,6 +35,7 @@ pre-commit = "3.3.1"
pytest = "7.3.1"
pytest-cov = "4.0.0"
types-chardet = "5.0.4.5"
types-colorama = { version = "0.4.15.11", markers = "sys_platform == 'win32'" }

[tool.poetry.group.docs.dependencies]
mkdocs = "1.4.3"
Expand Down
Loading

0 comments on commit e3608e5

Please sign in to comment.