diff --git a/prospector/formatters/base.py b/prospector/formatters/base.py index 2ad06dc7..f1185a0f 100644 --- a/prospector/formatters/base.py +++ b/prospector/formatters/base.py @@ -7,7 +7,7 @@ from pathlib import Path from typing import Any, Optional -from prospector.message import Message +from prospector.message import Location, Message class Formatter(ABC): @@ -27,16 +27,12 @@ def __init__( def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str: raise NotImplementedError - def _make_path(self, path: Path) -> str: - if self.paths_relative_to is None: - path = path.absolute() - elif path.is_absolute(): - path = path.relative_to(self.paths_relative_to) - return str(path) + def _make_path(self, location: Location) -> Path: + return location.relative_path(self.paths_relative_to) def _message_to_dict(self, message: Message) -> dict[str, Any]: loc = { - "path": self._make_path(message.location.path), + "path": str(self._make_path(message.location)), "module": message.location.module, "function": message.location.function, "line": message.location.line, diff --git a/prospector/formatters/emacs.py b/prospector/formatters/emacs.py index 8b941b1e..7889071a 100644 --- a/prospector/formatters/emacs.py +++ b/prospector/formatters/emacs.py @@ -9,7 +9,7 @@ def render_message(self, message: Message) -> str: output = [ "%s:%s:%d:" % ( - self._make_path(message.location.path), + self._make_path(message.location), message.location.line, (message.location.character or 0) + 1, ), diff --git a/prospector/formatters/grouped.py b/prospector/formatters/grouped.py index 751a560b..320c22a4 100644 --- a/prospector/formatters/grouped.py +++ b/prospector/formatters/grouped.py @@ -1,4 +1,5 @@ from collections import defaultdict +from pathlib import Path from prospector.formatters.text import TextFormatter from prospector.message import Message @@ -14,11 +15,11 @@ def render_messages(self) -> str: "", ] - groups: dict[str, dict[int, list[Message]]] = defaultdict(lambda: defaultdict(list)) + groups: dict[Path, dict[int, list[Message]]] = defaultdict(lambda: defaultdict(list)) for message in self.messages: assert message.location.line is not None - groups[self._make_path(message.location.path)][message.location.line].append(message) + groups[self._make_path(message.location)][message.location.line].append(message) for filename in sorted(groups.keys()): output.append(str(filename)) diff --git a/prospector/formatters/pylint.py b/prospector/formatters/pylint.py index c7ebe836..ffa6fd0e 100644 --- a/prospector/formatters/pylint.py +++ b/prospector/formatters/pylint.py @@ -11,14 +11,13 @@ class PylintFormatter(SummaryFormatter): on top of pylint and prospector itself. """ - def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str: - # this formatter will always ignore the summary and profile + def render_messages(self) -> list[str]: cur_loc = None output = [] for message in sorted(self.messages): if cur_loc != message.location.path: cur_loc = message.location.path - module_name = self._make_path(message.location.path).replace(os.path.sep, ".") + module_name = str(self._make_path(message.location)).replace(os.path.sep, ".") module_name = re.sub(r"(\.__init__)?\.py$", "", module_name) header = f"************* Module {module_name}" @@ -32,7 +31,7 @@ def render(self, summary: bool = True, messages: bool = True, profile: bool = Fa output.append( template % { - "path": self._make_path(message.location.path), + "path": self._make_path(message.location), "line": message.location.line, "source": message.source, "code": message.code, @@ -40,7 +39,12 @@ def render(self, summary: bool = True, messages: bool = True, profile: bool = Fa "message": message.message.strip(), } ) + return output + def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str: + output: list[str] = [] + if messages: + output.extend(self.render_messages()) if profile: output.append("") output.append(self.render_profile()) diff --git a/prospector/formatters/text.py b/prospector/formatters/text.py index 49bb40fc..269e4565 100644 --- a/prospector/formatters/text.py +++ b/prospector/formatters/text.py @@ -4,17 +4,14 @@ __all__ = ("TextFormatter",) -# pylint: disable=unnecessary-lambda - - class TextFormatter(SummaryFormatter): def render_message(self, message: Message) -> str: output = [] if message.location.module: - output.append(f"{message.location.module} ({self._make_path(message.location.path)}):") + output.append(f"{message.location.module} ({self._make_path(message.location)}):") else: - output.append(f"{self._make_path(message.location.path)}:") + output.append(f"{self._make_path(message.location)}:") output.append( " L{}:{} {}: {} - {}".format( diff --git a/prospector/formatters/vscode.py b/prospector/formatters/vscode.py index d99d0ce6..c4ffec41 100644 --- a/prospector/formatters/vscode.py +++ b/prospector/formatters/vscode.py @@ -9,15 +9,14 @@ class VSCodeFormatter(SummaryFormatter): This formatter outputs messages in the same way as vscode prospector linter expects. """ - def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str: - # this formatter will always ignore the summary and profile + def render_messages(self) -> list[str]: cur_loc = None output = [] for message in sorted(self.messages): if cur_loc != message.location.path: cur_loc = message.location.path - module_name = self._make_path(message.location.path).replace(os.path.sep, ".") + module_name = str(self._make_path(message.location)).replace(os.path.sep, ".") module_name = re.sub(r"(\.__init__)?\.py$", "", module_name) header = f"************* Module {module_name}" @@ -34,6 +33,12 @@ def render(self, summary: bool = True, messages: bool = True, profile: bool = Fa "message": message.message.strip(), } ) + return output + + def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str: + output: list[str] = [] + if messages: + output.extend(self.render_messages()) if profile: output.append("") output.append(self.render_profile()) diff --git a/prospector/formatters/xunit.py b/prospector/formatters/xunit.py index 8608322f..8379f74c 100644 --- a/prospector/formatters/xunit.py +++ b/prospector/formatters/xunit.py @@ -34,14 +34,14 @@ def render(self, summary: bool = True, messages: bool = True, profile: bool = Fa for message in sorted(self.messages): testcase_el = xml_doc.createElement("testcase") - testcase_el.setAttribute("name", f"{self._make_path(message.location.path)}-{message.location.line}") + testcase_el.setAttribute("name", f"{self._make_path(message.location)}-{message.location.line}") failure_el = xml_doc.createElement("error") failure_el.setAttribute("message", message.message.strip()) failure_el.setAttribute("type", f"{message.source} Error") template = "%(path)s:%(line)s: [%(code)s(%(source)s), %(function)s] %(message)s" cdata = template % { - "path": self._make_path(message.location.path), + "path": self._make_path(message.location), "line": message.location.line, "source": message.source, "code": message.code, diff --git a/prospector/message.py b/prospector/message.py index f0ac15d2..b902329b 100644 --- a/prospector/message.py +++ b/prospector/message.py @@ -12,9 +12,9 @@ def __init__( character: Optional[int], ): if isinstance(path, Path): - self._path = path + self._path = path.absolute() elif isinstance(path, str): - self._path = Path(path) + self._path = Path(path).absolute() else: raise ValueError self.module = module or None @@ -27,10 +27,10 @@ def path(self) -> Path: return self._path def absolute_path(self) -> Path: - return self._path.absolute() + return self._path - def relative_path(self, root: Path) -> Path: - return self._path.relative_to(root) + def relative_path(self, root: Optional[Path]) -> Path: + return self._path.relative_to(root) if root else self._path def __repr__(self) -> str: return f"{self._path}:L{self.line}:{self.character}"