Skip to content

Commit

Permalink
feat: add support for sorting baseline output
Browse files Browse the repository at this point in the history
  • Loading branch information
Alec Barber committed Apr 13, 2024
1 parent 31384a3 commit 600ea18
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 10 deletions.
5 changes: 5 additions & 0 deletions mypy_baseline/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Config:
depth: int = 40
allow_unsynced: bool = False
preserve_position: bool = False
sort_baseline: bool = False
hide_stats: bool = False
no_colors: bool = bool(os.environ.get('NO_COLOR'))
ignore: list[str] = dataclasses.field(default_factory=list)
Expand Down Expand Up @@ -68,6 +69,10 @@ def init_parser(self, parser: ArgumentParser) -> None:
'--preserve-position', action='store_true',
help='do not remove line number from the baseline.',
)
add(
'--sort-baseline', action='store_true',
help='sort the baseline file.',
)
add(
'--hide-stats', action='store_true',
help='do not show stats at the end.',
Expand Down
19 changes: 17 additions & 2 deletions mypy_baseline/_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,28 @@ def message(self) -> str:
def category(self) -> str:
return self._match.group('category') or 'note'

def get_clean_line(self, config: Config) -> str:
def get_clean_line_components(self, config: Config) -> ErrorLineComponents:
path = Path(*self.path.parts[:config.depth])
pos = self.line_number if config.preserve_position else 0
msg = REX_COLOR.sub('', self.message).strip()
msg = REX_COLOR_NBQA.sub('', msg).strip()
msg = REX_LINE_IN_MSG.sub('defined on line 0', msg)
line = f'{path}:{pos}: {self.severity}: {msg}'
return ErrorLineComponents(path, pos, self.severity, msg, self.category)

def get_clean_line(self, config: Config) -> str:
return self.get_clean_line_components(config).render()


@dataclass(order=True)
class ErrorLineComponents:
path: Path
pos: int
severity: str
msg: str
category: str

def render(self) -> str:
line = f'{self.path}:{self.pos}: {self.severity}: {self.msg}'
if self.category != 'note':
line += f' [{self.category}]'
return line
22 changes: 15 additions & 7 deletions mypy_baseline/commands/_sync.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from .._error import Error
from .._error import Error, ErrorLineComponents
from ._base import Command


Expand All @@ -15,7 +15,7 @@ def run(self) -> int:
baseline_text = ''
old_baseline = baseline_text.splitlines()

new_baseline: list[str] = []
new_baseline: list[ErrorLineComponents] = []
for line in self.stdin:
error = Error.new(line)
if error is None:
Expand All @@ -25,14 +25,18 @@ def run(self) -> int:
continue
if self.config.is_ignored_category(error.category):
continue
clean_line = error.get_clean_line(self.config)
clean_line = error.get_clean_line_components(self.config)
new_baseline.append(clean_line)

if self.config.sort_baseline:
new_baseline.sort()
new_baseline_lines = [components.render() for components in new_baseline]

synced = False
if old_baseline:
synced = self._stable_sync(old_baseline, new_baseline)
synced = self._stable_sync(old_baseline, new_baseline_lines)
if not synced:
self._write_baseline(new_baseline)
self._write_baseline(new_baseline_lines)
return 0

def _stable_sync(self, old_bline: list[str], new_bline: list[str]) -> bool:
Expand All @@ -44,9 +48,13 @@ def _stable_sync(self, old_bline: list[str], new_bline: list[str]) -> bool:
Currently, we can do a stable sync only when there are no new lines added.
It's hard to insert new lines in the correct positions, and adding them
at the end of the file will cause merge conflicts.
Sorting lines alphabetically would solve the issue, but I want to keep
backward compatibility.
Sorting lines solves the issue, so we don't use stable sync when the output
is sorted.
However, sorting is not enabled by default because I want to keep backward
compatibility.
"""
if not self.config.sort_baseline:
return False
old_set = set(old_bline)
new_set = set(new_bline)
removed = old_set - new_set
Expand Down
1 change: 1 addition & 0 deletions tests/test_commands/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
LINE1 = 'views.py:69: error: Hello world [assignment]\r\n'
LINE2 = 'settings.py:42: error: How are you? [union-attr]\r\n'
LINE3 = 'python/utils.py:15: error: Second argument of Enum() must be string [misc]\n'
LINE4 = 'python/utils.py:2: note: this line comes before LINE3\n'
LINE_WITH_NOTE = 'integrations/services.py:0: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports' # noqa: E501
SUCCESS_LINE = 'Success: no issues found in 26 source files'
NOTEBOOK_LINE1 = 'fail.ipynb:cell_1:2: \x1b[1m\x1b[31merror:\x1b(B\x1b[m Incompatible return value type (got \x1b(B\x1b[m\x1b[1m"int"\x1b(B\x1b[m, expected \x1b(B\x1b[m\x1b[1m"str"\x1b(B\x1b[m) \x1b(B\x1b[m\x1b[33m[return-value]\x1b(B\x1b[m\n' # noqa: E501
Expand Down
36 changes: 35 additions & 1 deletion tests/test_commands/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from mypy_baseline import main

from .helpers import LINE1, LINE2, LINE3, NOTEBOOK_LINE1
from .helpers import LINE1, LINE2, LINE3, LINE4, NOTEBOOK_LINE1


def test_sync(tmp_path: Path):
Expand Down Expand Up @@ -34,3 +34,37 @@ def test_sync_notebook(tmp_path: Path):
actual = blpath.read_text()
line1 = actual.splitlines()[0]
assert line1 == 'fail.ipynb:cell_1:0: error: Incompatible return value type (got "int", expected "str") [return-value]' # noqa: E501


def test_sync_sorted(tmp_path: Path):
blpath = tmp_path / 'bline.txt'
stdin = StringIO()
stdin.write(LINE1)
stdin.write(LINE2)
stdin.write(LINE3)
stdin.seek(0)
code = main(['sync', '--sort-baseline', '--baseline-path', str(blpath)], stdin, StringIO()) # noqa: E501
assert code == 0
actual = blpath.read_text()
line1, line2, line3 = actual.splitlines()
assert line1 == 'python/utils.py:0: error: Second argument of Enum() must be string [misc]' # noqa: E501s
assert line2 == 'settings.py:0: error: How are you? [union-attr]'
assert line3 == 'views.py:0: error: Hello world [assignment]'


def test_sync_sorted_line_numbers(tmp_path: Path):
blpath = tmp_path / 'bline.txt'
stdin = StringIO()
stdin.write(LINE1)
stdin.write(LINE2)
stdin.write(LINE3)
stdin.write(LINE4)
stdin.seek(0)
code = main(['sync', '--preserve-position', '--sort-baseline', '--baseline-path', str(blpath)], stdin, StringIO()) # noqa: E501
assert code == 0
actual = blpath.read_text()
line1, line2, line3, line4 = actual.splitlines()
assert line1 == 'python/utils.py:2: note: this line comes before LINE3'
assert line2 == 'python/utils.py:15: error: Second argument of Enum() must be string [misc]' # noqa: E501s
assert line3 == 'settings.py:42: error: How are you? [union-attr]'
assert line4 == 'views.py:69: error: Hello world [assignment]'

0 comments on commit 600ea18

Please sign in to comment.