Skip to content

Commit

Permalink
More type fixes (#4424)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssbarnea authored Nov 25, 2024
1 parent e83eb91 commit 11fa1d0
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 31 deletions.
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

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 @@ def matchtask(
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 @@ def matchtask(
"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 @@ def include_children(
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 @@ def __iter__(self) -> Iterator[str]:
"""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)
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

0 comments on commit 11fa1d0

Please sign in to comment.