Skip to content

Commit

Permalink
Refactor task iterator to return an object (#3195)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssbarnea committed Mar 16, 2023
1 parent ef483a0 commit 959851c
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 36 deletions.
29 changes: 15 additions & 14 deletions src/ansiblelint/rules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,23 +155,22 @@ def matchtasks(self, file: Lintable) -> list[MatchError]: # noqa: C901
):
return matches

tasks_iterator = ansiblelint.yaml_utils.iter_tasks_in_file(file)
for raw_task, task, skipped_tags, error in tasks_iterator:
if error is not None:
for task in ansiblelint.yaml_utils.iter_tasks_in_file(file):
if task.error is not None:
# normalize_task converts AnsibleParserError to MatchError
return [error]
return [task.error]

if (
self.id in skipped_tags
or ("action" not in task)
or "skip_ansible_lint" in task.get("tags", [])
self.id in task.skip_tags
or ("action" not in task.normalized_task)
or "skip_ansible_lint" in task.normalized_task.get("tags", [])
):
continue

if self.needs_raw_task:
task["__raw_task__"] = raw_task
task.normalized_task["__raw_task__"] = task.raw_task

result = self.matchtask(task, file=file)
result = self.matchtask(task.normalized_task, file=file)
if not result:
continue

Expand All @@ -181,13 +180,15 @@ def matchtasks(self, file: Lintable) -> list[MatchError]: # noqa: C901
# https://github.com/PyCQA/pylint/issues/6044
# pylint: disable=not-an-iterable
for match in result:
if match.tag in skipped_tags:
if match.tag in task.skip_tags:
continue
self._enrich_matcherror_with_task_details(match, task)
self._enrich_matcherror_with_task_details(
match, task.normalized_task
)
matches.append(match)
continue
if isinstance(result, MatchError):
if result.tag in skipped_tags:
if result.tag in task.skip_tags:
continue
match = result
else: # bool or string
Expand All @@ -196,11 +197,11 @@ def matchtasks(self, file: Lintable) -> list[MatchError]: # noqa: C901
message = result
match = self.create_matcherror(
message=message,
linenumber=task[LINE_NUMBER_KEY],
linenumber=task.normalized_task[LINE_NUMBER_KEY],
filename=file,
)

self._enrich_matcherror_with_task_details(match, task)
self._enrich_matcherror_with_task_details(match, task.normalized_task)
matches.append(match)
return matches

Expand Down
2 changes: 1 addition & 1 deletion src/ansiblelint/schemas/__store__.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"url": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/execution-environment.json"
},
"galaxy": {
"etag": "f9cae8231aa310ef1d26d46142097d1439b0d38f03029cfcdcf0c2830b24ca37",
"etag": "61f38feb51dc7eaff43ab22f3759b3a5202776ee75ee4204f07135282817f724",
"url": "https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/galaxy.json"
},
"inventory": {
Expand Down
52 changes: 31 additions & 21 deletions src/ansiblelint/yaml_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import os
import re
from collections.abc import Iterator, Sequence
from dataclasses import dataclass
from io import StringIO
from re import Pattern
from typing import TYPE_CHECKING, Any, Callable, Union, cast
Expand Down Expand Up @@ -110,28 +111,12 @@ def load_yamllint_config() -> YamlLintConfig:

def iter_tasks_in_file(
lintable: Lintable,
) -> Iterator[tuple[dict[str, Any], dict[str, Any], list[str], MatchError | None]]:
) -> Iterator[Task]:
"""Iterate over tasks in file.
This yields a 4-tuple of raw_task, normalized_task, skip_tags, and error.
raw_task:
When looping through the tasks in the file, each "raw_task" is minimally
processed to include these special keys: __line__, __file__, skipped_rules.
normalized_task:
When each raw_task is "normalized", action shorthand (strings) get parsed
by ansible into python objects and the action key gets normalized. If the task
should be skipped (skipped is True) or normalizing it fails (error is not None)
then this is just the raw_task instead of a normalized copy.
skip_tags:
List of tags found to be skipped, from tags block or noqa comments
error:
This is normally None. It will be a MatchError when the raw_task cannot be
normalized due to an AnsibleParserError.
:param lintable: The playbook or tasks/handlers yaml file to get tasks from
Yields raw_task, normalized_task, skipped, error
Yields a Task object
"""
data = lintable.data
if not data:
Expand All @@ -148,16 +133,16 @@ def iter_tasks_in_file(
normalized_task = normalize_task(raw_task, str(lintable.path))
except MatchError as err:
# normalize_task converts AnsibleParserError to MatchError
yield raw_task, raw_task, skip_tags, err
yield Task(raw_task, raw_task, skip_tags, err)
return

if "skip_ansible_lint" in raw_task.get("tags", []):
skip_tags.append("skip_ansible_lint")
if skip_tags:
yield raw_task, normalized_task, skip_tags, err
yield Task(raw_task, normalized_task, skip_tags, None)
continue

yield raw_task, normalized_task, skip_tags, err
yield Task(raw_task, normalized_task, skip_tags, None)


def nested_items_path(
Expand Down Expand Up @@ -1135,3 +1120,28 @@ def clean_json(
# neither a dict nor a list, do nothing
pass
return obj


@dataclass
class Task:
"""Class that represents a task from linter point of view.
raw_task:
When looping through the tasks in the file, each "raw_task" is minimally
processed to include these special keys: __line__, __file__, skipped_rules.
normalized_task:
When each raw_task is "normalized", action shorthand (strings) get parsed
by ansible into python objects and the action key gets normalized. If the task
should be skipped (skipped is True) or normalizing it fails (error is not None)
then this is just the raw_task instead of a normalized copy.
skip_tags:
List of tags found to be skipped, from tags block or noqa comments
error:
This is normally None. It will be a MatchError when the raw_task cannot be
normalized due to an AnsibleParserError.
"""

raw_task: tuple[dict[str, Any]]
normalized_task: dict[str, Any]
skip_tags: list[str]
error: MatchError | None = None

0 comments on commit 959851c

Please sign in to comment.