Skip to content
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
13 changes: 11 additions & 2 deletions src/ansiblelint/rules/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@
from ansiblelint.yaml_utils import clean_json

if TYPE_CHECKING:
from ansible.plugins.loader import PluginLoadContext

Check warning on line 29 in src/ansiblelint/rules/args.py

View check run for this annotation

Codecov / codecov/patch

src/ansiblelint/rules/args.py#L29

Added line #L29 was not covered by tests

from ansiblelint.errors import MatchError
from ansiblelint.file_utils import Lintable
from ansiblelint.utils import Task


_logger = logging.getLogger(__name__)

ignored_re = re.compile(
Expand Down Expand Up @@ -106,7 +107,7 @@
if module_name in self.module_aliases:
return []

loaded_module = load_plugin(module_name)
loaded_module: PluginLoadContext = load_plugin(module_name)

# https://github.com/ansible/ansible-lint/issues/3200
# since "ps1" modules cannot be executed on POSIX platforms, we will
Expand Down Expand Up @@ -144,6 +145,14 @@
"AnsibleModule",
CustomAnsibleModule,
):
if not loaded_module.plugin_resolved_name:
_logger.warning(
"Unable to load module %s at %s:%s for options validation",
module_name,
file.filename if file else None,
task[LINE_NUMBER_KEY],
)
return []
spec = importlib.util.spec_from_file_location(
name=loaded_module.plugin_resolved_name,
location=loaded_module.plugin_resolved_path,
Expand Down
26 changes: 6 additions & 20 deletions src/ansiblelint/rules/jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING, Any, NamedTuple
from typing import TYPE_CHECKING, NamedTuple

import black
import jinja2
from ansible.errors import AnsibleError, AnsibleFilterError, AnsibleParserError
from ansible.parsing.yaml.objects import AnsibleUnicode
from jinja2.exceptions import TemplateSyntaxError

from ansiblelint.constants import LINE_NUMBER_KEY
from ansiblelint.errors import RuleMatchTransformMeta
from ansiblelint.file_utils import Lintable
from ansiblelint.rules import AnsibleLintRule, TransformMixin
Expand Down Expand Up @@ -195,7 +194,7 @@ def matchtask(
result.append(
self.create_matcherror(
message=str(exc),
lineno=_get_error_line(task, path),
lineno=task.get_error_line(path),
filename=file,
tag=f"{self.id}[invalid]",
),
Expand All @@ -214,7 +213,7 @@ def matchtask(
value=v,
reformatted=reformatted,
),
lineno=_get_error_line(task, path),
lineno=task.get_error_line(path),
details=details,
filename=file,
tag=f"{self.id}[{tag}]",
Expand All @@ -233,12 +232,13 @@ def matchtask(

def matchyaml(self, file: Lintable) -> list[MatchError]:
"""Return matches for variables defined in vars files."""
data: dict[str, Any] = {}
raw_results: list[MatchError] = []
results: list[MatchError] = []

if str(file.kind) == "vars":
data = parse_yaml_from_file(str(file.path))
if not isinstance(data, dict):
return results
for key, v, _path in nested_items_path(data):
if isinstance(v, AnsibleUnicode):
reformatted, details, tag = self.check_whitespace(
Expand Down Expand Up @@ -406,7 +406,7 @@ def uncook(value: str, *, implicit: bool = False) -> str:
except jinja2.exceptions.TemplateSyntaxError as exc:
return "", str(exc.message), "invalid"
# pylint: disable=c-extension-no-member
except (NotImplementedError, black.parsing.InvalidInput) as exc:
except (NotImplementedError, ValueError) as exc:
# black is not able to recognize all valid jinja2 templates, so we
# just ignore InvalidInput errors.
# NotImplementedError is raised internally for expressions with
Expand Down Expand Up @@ -898,17 +898,3 @@ def _do_template(*args, **kwargs): # type: ignore[no-untyped-def] # Templar.do_
with mock.patch.object(Templar, "do_template", _do_template):
results = Runner(lintable, rules=collection).run()
assert len(results) == 0


def _get_error_line(task: dict[str, Any], path: list[str | int]) -> int:
"""Return error line number."""
line = task[LINE_NUMBER_KEY]
ctx = task
for _ in path:
ctx = ctx[_]
if LINE_NUMBER_KEY in ctx:
line = ctx[LINE_NUMBER_KEY]
if not isinstance(line, int):
msg = "Line number is not an integer"
raise TypeError(msg)
return line
10 changes: 8 additions & 2 deletions src/ansiblelint/rules/role_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,17 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
msg = "Role dependency has unexpected type."
raise TypeError(msg)
if "/" in role_name:
lineno = 1
if hasattr(role_name, "ansible_pos"):
lineno = role_name.ansible_pos[ # pyright: ignore[reportAttributeAccessIssue]
1
]

result.append(
self.create_matcherror(
f"Avoid using paths when importing roles. ({role_name})",
filename=file,
lineno=role_name.ansible_pos[1],
lineno=lineno,
tag=f"{self.id}[path]",
),
)
Expand Down Expand Up @@ -165,7 +171,7 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
def _infer_role_name(meta: Path, default: str) -> str:
if meta.is_file():
meta_data = parse_yaml_from_file(str(meta))
if meta_data:
if meta_data and isinstance(meta_data, dict):
try:
return str(meta_data["galaxy_info"]["role_name"])
except (KeyError, TypeError):
Expand Down
28 changes: 27 additions & 1 deletion src/ansiblelint/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from __future__ import annotations

import ast
import collections.abc
import contextlib
import inspect
import logging
Expand Down Expand Up @@ -306,7 +307,7 @@
v = v["file"]

# we cannot really parse any jinja2 in includes, so we ignore them
if not v or "{{" in v:
if not v or not isinstance(v, str) or "{{" in v:
return []

# handle include: filename.yml tags=blah
Expand Down Expand Up @@ -877,6 +878,31 @@
"""Provide support for 'key in task'."""
yield from (f for f in self.normalized_task)

def get_error_line(self, path: list[str | int]) -> int:
"""Return error line number."""
ctx = self.normalized_task
line = self.normalized_task[LINE_NUMBER_KEY]
for _ in path:
if (
isinstance(ctx, collections.abc.Container) and _ in ctx
): # isinstance(ctx, collections.abc.Container) and
value = ctx.get( # pyright: ignore[reportAttributeAccessIssue]
_ # pyright: ignore[reportArgumentType]
)
if isinstance(value, dict):
ctx = value
if (
isinstance(ctx, collections.abc.Container)
and LINE_NUMBER_KEY in ctx
):
line = ctx[LINE_NUMBER_KEY] # pyright: ignore[reportIndexIssue]
# else:
# break
if not isinstance(line, int):
msg = "Line number is not an integer"
raise TypeError(msg)

Check warning on line 903 in src/ansiblelint/utils.py

View check run for this annotation

Codecov / codecov/patch

src/ansiblelint/utils.py#L902-L903

Added lines #L902 - L903 were not covered by tests
return line


def task_in_list(
data: AnsibleBaseYAMLObject,
Expand Down
6 changes: 3 additions & 3 deletions src/ansiblelint/yaml_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,15 +244,15 @@ def _nested_items_path(
"""
# we have to cast each convert_to_tuples assignment or mypy complains
# that both assignments (for dict and list) do not have the same type
convert_to_tuples_type = Callable[[], Iterator[tuple[str | int, Any]]]
# convert_to_tuples_type = Callable[[], Iterator[tuple[str | int, Any]]]
if isinstance(data_collection, dict):
convert_data_collection_to_tuples = cast(
convert_to_tuples_type,
Callable[[], Iterator[tuple[str | int, Any]]],
functools.partial(data_collection.items),
)
elif isinstance(data_collection, list):
convert_data_collection_to_tuples = cast(
convert_to_tuples_type,
Callable[[], Iterator[tuple[str | int, Any]]],
functools.partial(enumerate, data_collection),
)
else:
Expand Down
4 changes: 2 additions & 2 deletions test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@
from _pytest.capture import CaptureFixture
from _pytest.logging import LogCaptureFixture
from _pytest.monkeypatch import MonkeyPatch
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject

from ansiblelint.rules import RulesCollection


runtime = Runtime(require_module=True)


Expand Down Expand Up @@ -237,7 +237,7 @@ def test_extract_from_list_recursive() -> None:
block = {
"block": [{"block": [{"name": "hello", "command": "whoami"}]}],
}
blocks = [block]
blocks: AnsibleBaseYAMLObject = [block]

test_list = utils.extract_from_list(blocks, ["block"])
assert list(block["block"]) == test_list
Expand Down
2 changes: 1 addition & 1 deletion test/test_yaml_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def test_tasks_in_list_empty_file(empty_lintable: Lintable) -> None:
assert empty_lintable.path
res = list(
task_in_list(
data=empty_lintable,
data=empty_lintable.data,
file=empty_lintable,
kind=empty_lintable.kind,
),
Expand Down
Loading