from __future__ import annotations import logging from dataclasses import dataclass 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() def _log_and_exit(self) -> None: self._log_violations(self.violations) self._log_total_number_of_violations_found(self.violations) def _log_total_number_of_violations_found(self, violations: list[Violation]) -> None: if violations: 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://deptry.com/") else: 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)) 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