Skip to content

Commit 6414db8

Browse files
author
Aleksey Petryankin
committed
Review-changes 4: fix score when use-local-configs=y
1 parent 655ef69 commit 6414db8

File tree

2 files changed

+70
-3
lines changed

2 files changed

+70
-3
lines changed

pylint/lint/pylinter.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
ModuleDescriptionDict,
7070
Options,
7171
)
72-
from pylint.utils import ASTWalker, FileState, LinterStats, utils
72+
from pylint.utils import ASTWalker, FileState, LinterStats, merge_stats, utils
7373

7474
MANAGER = astroid.MANAGER
7575

@@ -320,6 +320,7 @@ def __init__(
320320

321321
# Attributes related to stats
322322
self.stats = LinterStats()
323+
self.all_stats: list[LinterStats] = []
323324

324325
# Attributes related to (command-line) options and their parsing
325326
self.options: Options = options + _make_linter_options(self)
@@ -807,6 +808,11 @@ def _lint_files(
807808
)
808809
else:
809810
self.add_message("fatal", args=msg, confidence=HIGH)
811+
# current self.stats is needed in merge - it contains stats from last module
812+
finished_run_stats = merge_stats([*self.all_stats, self.stats])
813+
# after _lint_files linter.stats is aggregate stats from all modules, like after check_parallel
814+
self.all_stats = []
815+
self.stats = finished_run_stats
810816

811817
def _lint_file(
812818
self,
@@ -951,12 +957,17 @@ def _expand_files(
951957
def set_current_module(self, modname: str, filepath: str | None = None) -> None:
952958
"""Set the name of the currently analyzed module and
953959
init statistics for it.
960+
961+
Save current stats before init to make sure no counters for
962+
error, statement, etc are missed.
954963
"""
955964
if not modname and filepath is None:
956965
return
957966
self.reporter.on_set_current_module(modname or "", filepath)
958967
self.current_name = modname
959968
self.current_file = filepath or modname
969+
self.all_stats.append(self.stats)
970+
self.stats = LinterStats()
960971
self.stats.init_single_module(modname or "")
961972

962973
# If there is an actual filepath we might need to update the config attribute
@@ -1013,7 +1024,7 @@ def _astroid_module_checker(
10131024
rawcheckers=rawcheckers,
10141025
)
10151026

1016-
# notify global end
1027+
# notify end of module if jobs>1 or use-local-configs=y, global end otherwise
10171028
self.stats.statement = walker.nbstatements
10181029
for checker in reversed(_checkers):
10191030
checker.close()
@@ -1147,6 +1158,7 @@ def generate_reports(self, verbose: bool = False) -> int | None:
11471158
11481159
if persistent run, pickle results for later comparison
11491160
"""
1161+
self.config = self._base_config
11501162
# Display whatever messages are left on the reporter.
11511163
self.reporter.display_messages(report_nodes.Section())
11521164
if not self.file_state._is_base_filestate:

tests/config/test_per_directory_config.py

+56-1
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66

77
import os
88
import os.path
9+
from argparse import Namespace
910
from io import StringIO
1011
from pathlib import Path
12+
from typing import Any
13+
from unittest.mock import patch
1114

1215
import pytest
1316
from pytest import CaptureFixture
1417

1518
from pylint.lint import Run as LintRun
19+
from pylint.lint.pylinter import PyLinter
1620
from pylint.testutils._run import _Run as Run
1721
from pylint.testutils.utils import _patch_streams, _test_cwd
1822

@@ -52,7 +56,7 @@ def _create_subconfig_test_fs(tmp_path: Path) -> tuple[Path, ...]:
5256
level1_init.touch()
5357
level1_init2.touch()
5458
level2_init.touch()
55-
test_file_text = "#LEVEL1\n#LEVEL2\n#ALL_LEVELS\n#TODO\n"
59+
test_file_text = "#LEVEL1\n#LEVEL2\n#ALL_LEVELS\n#TODO\nassert (1, None)\ns = 'statement without warnings'\n"
5660
test_file1.write_text(test_file_text)
5761
test_file2.write_text(test_file_text)
5862
test_file3.write_text(test_file_text)
@@ -250,3 +254,54 @@ def test_local_config_verbose(
250254
LintRun(["--verbose", "--use-local-configs=y", str(tmp_files[1])], exit=False)
251255
output = capsys.readouterr()
252256
assert f"Using config from {level1_dir / 'sub'}" in output.err
257+
258+
259+
def ns_diff(ns1: Namespace, ns2: Namespace) -> str:
260+
msg = "Namespaces not equal\n"
261+
for k, v in ns1.__dict__.items():
262+
if v != ns2.__dict__[k]:
263+
msg += f"{v} != {ns2.__dict__[k]}\n"
264+
return msg
265+
266+
267+
generate_reports_orig = PyLinter.generate_reports
268+
269+
270+
def generate_reports_spy(self: PyLinter, *args: Any, **kwargs: Any) -> int:
271+
score = generate_reports_orig(self, *args, **kwargs)
272+
# check that generate_reports() worked with base config, not config from most recent module
273+
assert self.config == self._base_config, ns_diff(self.config, self._base_config)
274+
# level1_dir.a, level1_dir.z, level1_dir.sub.b from _create_subconfig_test_fs
275+
# each has 2 statements, one of which is warning => score should be 5
276+
assert score is not None
277+
assert 0 < score < 10
278+
return score
279+
280+
281+
@pytest.mark.parametrize(
282+
"local_config_args",
283+
[["--use-local-configs=y"], ["--use-local-configs=y", "--jobs=2"]],
284+
)
285+
def test_subconfigs_score(
286+
_create_subconfig_test_fs: tuple[Path, ...],
287+
local_config_args: list[str],
288+
) -> None:
289+
"""Check that statements from all checked modules are accounted in score:
290+
given stats from many modules such that
291+
total # of messages > statements in last module,
292+
check that score is >0 and <10.
293+
"""
294+
level1_dir, *_ = _create_subconfig_test_fs
295+
out = StringIO()
296+
with patch(
297+
"pylint.lint.run.Run.LinterClass.generate_reports",
298+
side_effect=generate_reports_spy,
299+
autospec=True,
300+
) as reports_patch, _patch_streams(out):
301+
linter = LintRun([*local_config_args, str(level1_dir)], exit=False).linter
302+
reports_patch.assert_called_once()
303+
304+
# level1_dir.a, level1_dir.z, level1_dir.sub.b from _create_subconfig_test_fs
305+
# each has 2 statements, one of which is warning, so 3 warnings total
306+
assert linter.stats.statement == 6
307+
assert linter.stats.warning == 3

0 commit comments

Comments
 (0)