Skip to content

Move task_to_str to Task class #4553

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/ansiblelint/rules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def _enrich_matcherror_with_task_details(
) -> None:
match.task = task
if not match.details:
match.details = "Task/Handler: " + ansiblelint.utils.task_to_str(task)
match.details = "Task/Handler: " + str(task)

match.lineno = max(match.lineno, task[LINE_NUMBER_KEY])

Expand Down
84 changes: 44 additions & 40 deletions src/ansiblelint/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ def parse_yaml_from_file(filepath: str) -> AnsibleBaseYAMLObject: # type: ignor
"""Extract a decrypted YAML object from file."""
dataloader = DataLoader()
if hasattr(dataloader, "set_vault_secrets"):
dataloader.set_vault_secrets(
[("default", PromptVaultSecret(_bytes=to_bytes(DEFAULT_VAULT_PASSWORD)))]
)
dataloader.set_vault_secrets([
("default", PromptVaultSecret(_bytes=to_bytes(DEFAULT_VAULT_PASSWORD)))
])

return dataloader.load_from_file(filepath)

Expand Down Expand Up @@ -618,7 +618,7 @@ def _get_task_handler_children_for_tasks_or_playbooks(
basedir = os.path.dirname(basedir)
f = path_dwim(basedir, file_name)
return Lintable(f, kind=child_type)
msg = f'The node contains none of: {", ".join(sorted(INCLUSION_ACTION_NAMES))}'
msg = f"The node contains none of: {', '.join(sorted(INCLUSION_ACTION_NAMES))}"
raise LookupError(msg)


Expand Down Expand Up @@ -712,37 +712,6 @@ def normalize_task_v2(task: Task) -> dict[str, Any]:
return result


def task_to_str(task: dict[str, Any]) -> str:
"""Make a string identifier for the given task."""
name = task.get("name")
if name:
return str(name)
action = task.get("action")
if isinstance(action, str) or not isinstance(action, dict):
return str(action)
args = [
f"{k}={v}"
for (k, v) in action.items()
if k
not in [
"__ansible_module__",
"__ansible_module_original__",
"_raw_params",
LINE_NUMBER_KEY,
FILENAME_KEY,
]
]

raw_params = action.get("_raw_params", [])
if isinstance(raw_params, list):
for item in raw_params:
args.extend(str(item))
else:
args.append(raw_params)

return f"{action['__ansible_module__']} {' '.join(args)}"


# pylint: disable=too-many-nested-blocks
def extract_from_list( # type: ignore[no-any-unimported]
blocks: AnsibleBaseYAMLObject,
Expand Down Expand Up @@ -790,14 +759,16 @@ class Task(dict[str, Any]):
error:
This is normally None. It will be a MatchError when the raw_task cannot be
normalized due to an AnsibleParserError.
position: Any
position:
The position of the task in the data structure using JSONPath like
notation (no $ prefix).
"""

raw_task: dict[str, Any]
filename: str = ""
_normalized_task: dict[str, Any] | _MISSING_TYPE = field(init=False, repr=False)
error: MatchError | None = None
position: Any = None
position: str = ""

@property
def name(self) -> str | None:
Expand Down Expand Up @@ -875,9 +846,43 @@ def is_handler(self) -> bool:
is_handler_file = "handlers" in paths
return is_handler_file or ".handlers[" in self.position

def __str__(self) -> str:
"""Make a string identifier for the given task."""
name = self.get("name")
if name:
return str(name)
action = self.get("action")
if isinstance(action, str) or not isinstance(action, dict):
return str(action)
args = [
f"{k}={v}"
for (k, v) in action.items()
if k
not in [
"__ansible_module__",
"__ansible_module_original__",
"_raw_params",
LINE_NUMBER_KEY,
FILENAME_KEY,
]
]

raw_params = action.get("_raw_params", [])
if isinstance(raw_params, list):
for item in raw_params:
args.extend(str(item))
else:
args.append(raw_params)

return f"{action['__ansible_module__']} {' '.join(args)}"

def __repr__(self) -> str:
"""Return a string representation of the task."""
return f"Task('{self.name}' [{self.position}])"
result = f"Task('{self.name or self.action}'"
if self.position:
result += f" [{self.position}])"
result += ")"
return result

def get(self, key: str, default: Any = None) -> Any:
"""Get a value from the task."""
Expand Down Expand Up @@ -926,7 +931,6 @@ def task_in_list( # type: ignore[no-any-unimported]
"""Get action tasks from block structures."""

def each_entry(data: AnsibleBaseYAMLObject, position: str) -> Iterator[Task]: # type: ignore[no-any-unimported]

if not data or not isinstance(data, Iterable):
return
for entry_index, entry in enumerate(data):
Expand Down Expand Up @@ -958,7 +962,7 @@ def each_entry(data: AnsibleBaseYAMLObject, position: str) -> Iterator[Task]: #
if isinstance(item[attribute], list):
yield from each_entry(
item[attribute],
f"{position }[{item_index}].{attribute}",
f"{position}[{item_index}].{attribute}",
)
elif item[attribute] is not None:
msg = f"Key '{attribute}' defined, but bad value: '{item[attribute]!s}'"
Expand Down
3 changes: 1 addition & 2 deletions test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,7 @@ def test_template(template: str, output: str) -> None:
def test_task_to_str_unicode() -> None:
"""Ensure that extracting messages from tasks preserves Unicode."""
task = utils.Task({"fail": {"msg": "unicode é ô à"}}, filename="filename.yml")
result = utils.task_to_str(task._normalize_task()) # noqa: SLF001
assert result == "fail msg=unicode é ô à"
assert str(task) == "fail msg=unicode é ô à"


def test_logger_debug(caplog: LogCaptureFixture) -> None:
Expand Down