Skip to content

Commit 4f65fff

Browse files
Extract Comparator class from CompareCommand
Move JSON loading, batching, and cross-referencing logic into a dedicated Comparator class. CompareCommand.run() now delegates to the Comparator and iterates over it to build the comment.
1 parent d609085 commit 4f65fff

9 files changed

Lines changed: 125 additions & 59 deletions

File tree

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
2+
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
3+
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
4+
5+
from __future__ import annotations
6+
7+
import json
8+
from collections.abc import Generator
9+
from pathlib import Path
10+
11+
from pylint.reporters.json_reporter import OldJsonExport
12+
from pylint.testutils._primer.primer_command import (
13+
PackageData,
14+
PackageMessages,
15+
)
16+
17+
18+
class Comparator:
19+
def __init__(
20+
self, base_file: str, new_file: str, batches: int | None = None
21+
) -> None:
22+
main_data: PackageMessages
23+
if batches is None:
24+
main_data = self._load_json(base_file)
25+
self.pr_data = self._load_json(new_file)
26+
else:
27+
main_data = {}
28+
self.pr_data = {}
29+
for idx in range(batches):
30+
main_data.update(
31+
self._load_json(base_file.replace("BATCHIDX", "batch" + str(idx)))
32+
)
33+
self.pr_data.update(
34+
self._load_json(new_file.replace("BATCHIDX", "batch" + str(idx)))
35+
)
36+
37+
self.missing_messages: PackageMessages = {}
38+
for package, data in main_data.items():
39+
package_missing_messages: list[OldJsonExport] = []
40+
for message in data["messages"]:
41+
try:
42+
self.pr_data[package]["messages"].remove(message)
43+
except ValueError:
44+
package_missing_messages.append(message)
45+
self.missing_messages[package] = PackageData(
46+
commit=self.pr_data[package]["commit"],
47+
messages=package_missing_messages,
48+
)
49+
50+
def __iter__(
51+
self,
52+
) -> Generator[tuple[str, PackageData, PackageData]]:
53+
for package, missing_messages in self.missing_messages.items():
54+
new_messages = self.pr_data[package]
55+
if not missing_messages["messages"] and not new_messages["messages"]:
56+
continue
57+
yield package, missing_messages, new_messages
58+
59+
@staticmethod
60+
def _load_json(file_path: Path | str) -> PackageMessages:
61+
with open(file_path, encoding="utf-8") as f:
62+
result: PackageMessages = json.load(f)
63+
return result

pylint/testutils/_primer/primer_compare_command.py

Lines changed: 8 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,79 +3,28 @@
33
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
44
from __future__ import annotations
55

6-
import json
7-
from pathlib import Path, PurePosixPath
6+
from pathlib import PurePosixPath
87

9-
from pylint.reporters.json_reporter import OldJsonExport
10-
from pylint.testutils._primer.primer_command import (
11-
PackageData,
12-
PackageMessages,
13-
PrimerCommand,
14-
)
8+
from pylint.testutils._primer.comparator import Comparator
9+
from pylint.testutils._primer.primer_command import PackageData, PrimerCommand
1510

1611
MAX_GITHUB_COMMENT_LENGTH = 65536
1712

1813

1914
class CompareCommand(PrimerCommand):
2015
def run(self) -> None:
21-
if self.config.batches is None:
22-
main_data = self._load_json(self.config.base_file)
23-
pr_data = self._load_json(self.config.new_file)
24-
else:
25-
main_data = {}
26-
pr_data = {}
27-
for idx in range(self.config.batches):
28-
main_data.update(
29-
self._load_json(
30-
self.config.base_file.replace("BATCHIDX", "batch" + str(idx))
31-
)
32-
)
33-
pr_data.update(
34-
self._load_json(
35-
self.config.new_file.replace("BATCHIDX", "batch" + str(idx))
36-
)
37-
)
38-
39-
missing_messages_data, new_messages_data = self._cross_reference(
40-
main_data, pr_data
16+
comparator = Comparator(
17+
self.config.base_file, self.config.new_file, self.config.batches
4118
)
42-
comment = self._create_comment(missing_messages_data, new_messages_data)
19+
comment = self._create_comment(comparator)
4320
with open(self.primer_directory / "comment.txt", "w", encoding="utf-8") as f:
4421
f.write(comment)
4522

46-
@staticmethod
47-
def _cross_reference(
48-
main_data: PackageMessages, pr_data: PackageMessages
49-
) -> tuple[PackageMessages, PackageMessages]:
50-
missing_messages_data: PackageMessages = {}
51-
for package, data in main_data.items():
52-
package_missing_messages: list[OldJsonExport] = []
53-
for message in data["messages"]:
54-
try:
55-
pr_data[package]["messages"].remove(message)
56-
except ValueError:
57-
package_missing_messages.append(message)
58-
missing_messages_data[package] = PackageData(
59-
commit=pr_data[package]["commit"], messages=package_missing_messages
60-
)
61-
return missing_messages_data, pr_data
62-
63-
@staticmethod
64-
def _load_json(file_path: Path | str) -> PackageMessages:
65-
with open(file_path, encoding="utf-8") as f:
66-
result: PackageMessages = json.load(f)
67-
return result
68-
69-
def _create_comment(
70-
self, all_missing_messages: PackageMessages, all_new_messages: PackageMessages
71-
) -> str:
23+
def _create_comment(self, comparator: Comparator) -> str:
7224
comment = ""
73-
for package, missing_messages in all_missing_messages.items():
25+
for package, missing_messages, new_messages in comparator:
7426
if len(comment) >= MAX_GITHUB_COMMENT_LENGTH:
7527
break
76-
new_messages = all_new_messages[package]
77-
if not missing_messages["messages"] and not new_messages["messages"]:
78-
continue
7928
comment += self._create_comment_for_package(
8029
package, new_messages, missing_messages
8130
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{ "package": "astroid", "missing": 2, "new": 0 }]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{ "package": "astroid", "missing": 1, "new": 1 }]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{ "package": "astroid", "missing": 0, "new": 1 }]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{ "package": "astroid", "missing": 1, "new": 0 }]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
2+
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
3+
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
4+
5+
import json
6+
from pathlib import Path
7+
8+
import pytest
9+
10+
from pylint.testutils._primer.comparator import Comparator
11+
12+
FIXTURES_PATH = Path(__file__).parent / "fixtures"
13+
14+
15+
@pytest.mark.parametrize(
16+
"directory",
17+
[
18+
pytest.param(p, id=p.name)
19+
for p in FIXTURES_PATH.iterdir()
20+
if p.is_dir() and p.name != "batched" # tested separately
21+
],
22+
)
23+
def test_comparator(directory: Path) -> None:
24+
"""Test Comparator with each fixture directory."""
25+
comparator = Comparator(str(directory / "main.json"), str(directory / "pr.json"))
26+
expected = json.loads((directory / "expected_comparator.json").read_text("utf-8"))
27+
results = list(comparator)
28+
assert len(results) == len(expected)
29+
for (package, missing, new), exp in zip(results, expected):
30+
assert package == exp["package"]
31+
assert len(missing["messages"]) == exp["missing"]
32+
assert len(new["messages"]) == exp["new"]
33+
34+
35+
def test_comparator_batched() -> None:
36+
fixture = FIXTURES_PATH / "batched"
37+
comparator = Comparator(
38+
str(fixture / "main_BATCHIDX.json"),
39+
str(fixture / "pr_BATCHIDX.json"),
40+
batches=2,
41+
)
42+
expected = json.loads((fixture / "expected_comparator.json").read_text("utf-8"))
43+
results = list(comparator)
44+
assert len(results) == len(expected)
45+
for (package, missing, new), exp in zip(results, expected):
46+
assert package == exp["package"]
47+
assert len(missing["messages"]) == exp["missing"]
48+
assert len(new["messages"]) == exp["new"]

0 commit comments

Comments
 (0)