Skip to content

Commit 0920931

Browse files
cidrblockssbarnea
andauthored
Prune reported errors after autofix (#3774)
Co-authored-by: Sorin Sbarnea <[email protected]>
1 parent 59f4141 commit 0920931

File tree

13 files changed

+201
-65
lines changed

13 files changed

+201
-65
lines changed

.github/workflows/tox.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:GITHUB_STEP_SUMMARY
7272
# Number of expected test passes, safety measure for accidental skip of
7373
# tests. Update value if you add/remove tests.
74-
PYTEST_REQPASS: 827
74+
PYTEST_REQPASS: 828
7575
steps:
7676
- name: Activate WSL1
7777
if: "contains(matrix.shell, 'wsl')"

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ max-statements = 60
145145
disable = [
146146
# Disabled on purpose:
147147
"line-too-long", # covered by black
148+
"protected-access", # covered by ruff SLF001
148149
"too-many-branches", # covered by ruff C901
149150
# TODO(ssbarnea): remove temporary skips adding during initial adoption:
150151
"duplicate-code",

src/ansiblelint/__main__.py

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
)
5656
from ansiblelint.constants import RC
5757
from ansiblelint.loaders import load_ignore_txt
58+
from ansiblelint.runner import get_matches
5859
from ansiblelint.skip_utils import normalize_tag
5960
from ansiblelint.version import __version__
6061

@@ -202,6 +203,74 @@ def support_banner() -> None:
202203
)
203204

204205

206+
def fix(runtime_options: Options, result: LintResult, rules: RulesCollection) -> None:
207+
"""Fix the linting errors.
208+
209+
:param options: Options object
210+
:param result: LintResult object
211+
"""
212+
match_count = len(result.matches)
213+
_logger.debug("Begin fixing: %s matches", match_count)
214+
ruamel_safe_version = "0.17.26"
215+
216+
# pylint: disable=import-outside-toplevel
217+
from packaging.version import Version
218+
from ruamel.yaml import __version__ as ruamel_yaml_version_str
219+
220+
# pylint: enable=import-outside-toplevel
221+
222+
if Version(ruamel_safe_version) > Version(ruamel_yaml_version_str):
223+
_logger.warning(
224+
"We detected use of `--fix` feature with a buggy ruamel-yaml %s library instead of >=%s, upgrade it before reporting any bugs like dropped comments.",
225+
ruamel_yaml_version_str,
226+
ruamel_safe_version,
227+
)
228+
acceptable_tags = {"all", "none", *rules.known_tags()}
229+
unknown_tags = set(options.write_list).difference(acceptable_tags)
230+
231+
if unknown_tags:
232+
_logger.error(
233+
"Found invalid value(s) (%s) for --fix arguments, must be one of: %s",
234+
", ".join(unknown_tags),
235+
", ".join(acceptable_tags),
236+
)
237+
sys.exit(RC.INVALID_CONFIG)
238+
_do_transform(result, options)
239+
240+
rerun = ["yaml"]
241+
resolved = []
242+
for idx, match in reversed(list(enumerate(result.matches))):
243+
_logger.debug("Fixing: (%s of %s) %s", match_count - idx, match_count, match)
244+
if match.fixed:
245+
_logger.debug("Fixed, removed: %s", match)
246+
result.matches.pop(idx)
247+
continue
248+
if match.rule.id not in rerun:
249+
_logger.debug("Not rerun eligible: %s", match)
250+
continue
251+
252+
uid = (match.rule.id, match.filename)
253+
if uid in resolved:
254+
_logger.debug("Previously resolved: %s", match)
255+
result.matches.pop(idx)
256+
continue
257+
_logger.debug("Rerunning: %s", match)
258+
runtime_options.tags = [match.rule.id]
259+
runtime_options.lintables = [match.filename]
260+
runtime_options._skip_ansible_syntax_check = True # noqa: SLF001
261+
new_results = get_matches(rules, runtime_options)
262+
if not new_results.matches:
263+
_logger.debug("Newly resolved: %s", match)
264+
result.matches.pop(idx)
265+
resolved.append(uid)
266+
continue
267+
if match in new_results.matches:
268+
_logger.debug("Still found: %s", match)
269+
continue
270+
_logger.debug("Fixed, removed: %s", match)
271+
result.matches.pop(idx)
272+
273+
205274
# pylint: disable=too-many-statements,too-many-locals
206275
def main(argv: list[str] | None = None) -> int:
207276
"""Linter CLI entry point."""
@@ -244,7 +313,6 @@ def main(argv: list[str] | None = None) -> int:
244313

245314
# pylint: disable=import-outside-toplevel
246315
from ansiblelint.rules import RulesCollection
247-
from ansiblelint.runner import _get_matches
248316

249317
if options.list_profiles:
250318
from ansiblelint.generate_docs import profiles_as_rich
@@ -265,30 +333,7 @@ def main(argv: list[str] | None = None) -> int:
265333

266334
if isinstance(options.tags, str):
267335
options.tags = options.tags.split(",") # pragma: no cover
268-
result = _get_matches(rules, options)
269-
270-
if options.write_list:
271-
ruamel_safe_version = "0.17.26"
272-
from packaging.version import Version
273-
from ruamel.yaml import __version__ as ruamel_yaml_version_str
274-
275-
if Version(ruamel_safe_version) > Version(ruamel_yaml_version_str):
276-
_logger.warning(
277-
"We detected use of `--fix` feature with a buggy ruamel-yaml %s library instead of >=%s, upgrade it before reporting any bugs like dropped comments.",
278-
ruamel_yaml_version_str,
279-
ruamel_safe_version,
280-
)
281-
acceptable_tags = {"all", "none", *rules.known_tags()}
282-
unknown_tags = set(options.write_list).difference(acceptable_tags)
283-
284-
if unknown_tags:
285-
_logger.error(
286-
"Found invalid value(s) (%s) for --fix arguments, must be one of: %s",
287-
", ".join(unknown_tags),
288-
", ".join(acceptable_tags),
289-
)
290-
sys.exit(RC.INVALID_CONFIG)
291-
_do_transform(result, options)
336+
result = get_matches(rules, options)
292337

293338
mark_as_success = True
294339

@@ -302,6 +347,10 @@ def main(argv: list[str] | None = None) -> int:
302347
for match in result.matches:
303348
if match.tag in ignore_map[match.filename]:
304349
match.ignored = True
350+
_logger.debug("Ignored: %s", match)
351+
352+
if options.write_list:
353+
fix(runtime_options=options, result=result, rules=rules)
305354

306355
app.render_matches(result.matches)
307356

src/ansiblelint/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@
107107
class Options: # pylint: disable=too-many-instance-attributes,too-few-public-methods
108108
"""Store ansible-lint effective configuration options."""
109109

110+
# Private attributes
111+
_skip_ansible_syntax_check: bool = False
112+
113+
# Public attributes
110114
cache_dir: Path | None = None
111115
colored: bool = True
112116
configured: bool = False

src/ansiblelint/errors.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ def __repr__(self) -> str:
134134
self.details,
135135
)
136136

137+
def __str__(self) -> str:
138+
"""Return a MatchError instance string representation."""
139+
return self.__repr__()
140+
137141
@property
138142
def position(self) -> str:
139143
"""Return error positioning, with column number if available."""

src/ansiblelint/rules/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,10 +486,13 @@ def run(
486486
or rule.has_dynamic_tags
487487
or not set(rule.tags).union([rule.id]).isdisjoint(tags)
488488
):
489+
_logger.debug("Running rule %s", rule.id)
489490
rule_definition = set(rule.tags)
490491
rule_definition.add(rule.id)
491492
if set(rule_definition).isdisjoint(skip_list):
492493
matches.extend(rule.getmatches(file))
494+
else:
495+
_logger.debug("Skipping rule %s", rule.id)
493496

494497
# some rules can produce matches with tags that are inside our
495498
# skip_list, so we need to cleanse the matches

src/ansiblelint/rules/deprecated_local_action.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from typing import TYPE_CHECKING
1010

1111
from ansiblelint.rules import AnsibleLintRule, TransformMixin
12-
from ansiblelint.runner import _get_matches
12+
from ansiblelint.runner import get_matches
1313
from ansiblelint.transformer import Transformer
1414

1515
if TYPE_CHECKING:
@@ -107,7 +107,7 @@ def test_local_action_transform(
107107
config_options.write_list = ["all"]
108108

109109
config_options.lintables = [playbook]
110-
runner_result = _get_matches(
110+
runner_result = get_matches(
111111
rules=default_rules_collection,
112112
options=config_options,
113113
)

src/ansiblelint/rules/jinja.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from ansiblelint.errors import RuleMatchTransformMeta
1919
from ansiblelint.file_utils import Lintable
2020
from ansiblelint.rules import AnsibleLintRule, TransformMixin
21+
from ansiblelint.runner import get_matches
2122
from ansiblelint.skip_utils import get_rule_skips_from_line
2223
from ansiblelint.text import has_jinja
2324
from ansiblelint.utils import parse_yaml_from_file, template
@@ -482,10 +483,7 @@ def blacken(text: str) -> str:
482483
import pytest
483484

484485
from ansiblelint.rules import RulesCollection # pylint: disable=ungrouped-imports
485-
from ansiblelint.runner import (
486-
Runner,
487-
_get_matches,
488-
)
486+
from ansiblelint.runner import Runner
489487

490488
# pylint: disable=ungrouped-imports
491489
from ansiblelint.transformer import Transformer # pylint: disable=ungrouped-imports
@@ -840,7 +838,7 @@ def test_jinja_transform(
840838
config_options.write_list = ["all"]
841839

842840
config_options.lintables = [playbook]
843-
runner_result = _get_matches(
841+
runner_result = get_matches(
844842
rules=default_rules_collection,
845843
options=config_options,
846844
)

src/ansiblelint/rules/no_log_password.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from typing import TYPE_CHECKING
2020

2121
from ansiblelint.rules import AnsibleLintRule, RulesCollection, TransformMixin
22-
from ansiblelint.runner import _get_matches
22+
from ansiblelint.runner import get_matches
2323
from ansiblelint.transformer import Transformer
2424
from ansiblelint.utils import Task, convert_to_boolean
2525

@@ -336,7 +336,7 @@ def test_no_log_password_transform(
336336
rules.register(NoLogPasswordsRule())
337337

338338
config_options.lintables = [playbook]
339-
runner_result = _get_matches(rules=rules, options=config_options)
339+
runner_result = get_matches(rules=rules, options=config_options)
340340
transformer = Transformer(result=runner_result, options=config_options)
341341
transformer.run()
342342

src/ansiblelint/runner.py

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,13 @@ def __init__(
7979
verbosity: int = 0,
8080
checked_files: set[Lintable] | None = None,
8181
project_dir: str | None = None,
82+
_skip_ansible_syntax_check: bool = False,
8283
) -> None:
8384
"""Initialize a Runner instance."""
8485
self.rules = rules
8586
self.lintables: set[Lintable] = set()
8687
self.project_dir = os.path.abspath(project_dir) if project_dir else None
88+
self.skip_ansible_syntax_check = _skip_ansible_syntax_check
8789

8890
if skip_list is None:
8991
skip_list = []
@@ -234,32 +236,36 @@ def _run(self) -> list[MatchError]:
234236
)
235237

236238
# -- phase 1 : syntax check in parallel --
237-
app = get_app(offline=True)
238-
239-
def worker(lintable: Lintable) -> list[MatchError]:
240-
# pylint: disable=protected-access
241-
return self._get_ansible_syntax_check_matches(
242-
lintable=lintable,
243-
app=app,
244-
)
239+
if not self.skip_ansible_syntax_check:
240+
app = get_app(offline=True)
241+
242+
def worker(lintable: Lintable) -> list[MatchError]:
243+
# pylint: disable=protected-access
244+
return self._get_ansible_syntax_check_matches(
245+
lintable=lintable,
246+
app=app,
247+
)
245248

246-
for lintable in self.lintables:
247-
if lintable.kind not in ("playbook", "role") or lintable.stop_processing:
248-
continue
249-
files.append(lintable)
249+
for lintable in self.lintables:
250+
if (
251+
lintable.kind not in ("playbook", "role")
252+
or lintable.stop_processing
253+
):
254+
continue
255+
files.append(lintable)
250256

251-
# avoid resource leak warning, https://github.com/python/cpython/issues/90549
252-
# pylint: disable=unused-variable
253-
global_resource = multiprocessing.Semaphore() # noqa: F841
257+
# avoid resource leak warning, https://github.com/python/cpython/issues/90549
258+
# pylint: disable=unused-variable
259+
global_resource = multiprocessing.Semaphore() # noqa: F841
254260

255-
pool = multiprocessing.pool.ThreadPool(processes=threads())
256-
return_list = pool.map(worker, files, chunksize=1)
257-
pool.close()
258-
pool.join()
259-
for data in return_list:
260-
matches.extend(data)
261+
pool = multiprocessing.pool.ThreadPool(processes=threads())
262+
return_list = pool.map(worker, files, chunksize=1)
263+
pool.close()
264+
pool.join()
265+
for data in return_list:
266+
matches.extend(data)
261267

262-
matches = self._filter_excluded_matches(matches)
268+
matches = self._filter_excluded_matches(matches)
263269
# -- phase 2 ---
264270
if not matches:
265271
# do our processing only when ansible syntax check passed in order
@@ -585,7 +591,13 @@ def threads() -> int:
585591
return os_cpu_count
586592

587593

588-
def _get_matches(rules: RulesCollection, options: Options) -> LintResult:
594+
def get_matches(rules: RulesCollection, options: Options) -> LintResult:
595+
"""Get matches for given rules and options.
596+
597+
:param rules: Rules to use for linting.
598+
:param options: Options to use for linting.
599+
:returns: LintResult containing matches and checked files.
600+
"""
589601
lintables = ansiblelint.utils.get_lintables(opts=options, args=options.lintables)
590602

591603
for rule in rules:
@@ -605,6 +617,7 @@ def _get_matches(rules: RulesCollection, options: Options) -> LintResult:
605617
verbosity=options.verbosity,
606618
checked_files=checked_files,
607619
project_dir=options.project_dir,
620+
_skip_ansible_syntax_check=options._skip_ansible_syntax_check, # noqa: SLF001
608621
)
609622
matches.extend(runner.run())
610623

0 commit comments

Comments
 (0)