Skip to content

Commit 11fa1d0

Browse files
authored
More type fixes (#4424)
1 parent e83eb91 commit 11fa1d0

File tree

7 files changed

+58
-31
lines changed

7 files changed

+58
-31
lines changed

src/ansiblelint/rules/args.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@
2626
from ansiblelint.yaml_utils import clean_json
2727

2828
if TYPE_CHECKING:
29+
from ansible.plugins.loader import PluginLoadContext
30+
2931
from ansiblelint.errors import MatchError
3032
from ansiblelint.file_utils import Lintable
3133
from ansiblelint.utils import Task
3234

33-
3435
_logger = logging.getLogger(__name__)
3536

3637
ignored_re = re.compile(
@@ -106,7 +107,7 @@ def matchtask(
106107
if module_name in self.module_aliases:
107108
return []
108109

109-
loaded_module = load_plugin(module_name)
110+
loaded_module: PluginLoadContext = load_plugin(module_name)
110111

111112
# https://github.com/ansible/ansible-lint/issues/3200
112113
# since "ps1" modules cannot be executed on POSIX platforms, we will
@@ -144,6 +145,14 @@ def matchtask(
144145
"AnsibleModule",
145146
CustomAnsibleModule,
146147
):
148+
if not loaded_module.plugin_resolved_name:
149+
_logger.warning(
150+
"Unable to load module %s at %s:%s for options validation",
151+
module_name,
152+
file.filename if file else None,
153+
task[LINE_NUMBER_KEY],
154+
)
155+
return []
147156
spec = importlib.util.spec_from_file_location(
148157
name=loaded_module.plugin_resolved_name,
149158
location=loaded_module.plugin_resolved_path,

src/ansiblelint/rules/jinja.py

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@
88
import sys
99
from dataclasses import dataclass
1010
from pathlib import Path
11-
from typing import TYPE_CHECKING, Any, NamedTuple
11+
from typing import TYPE_CHECKING, NamedTuple
1212

1313
import black
1414
import jinja2
1515
from ansible.errors import AnsibleError, AnsibleFilterError, AnsibleParserError
1616
from ansible.parsing.yaml.objects import AnsibleUnicode
1717
from jinja2.exceptions import TemplateSyntaxError
1818

19-
from ansiblelint.constants import LINE_NUMBER_KEY
2019
from ansiblelint.errors import RuleMatchTransformMeta
2120
from ansiblelint.file_utils import Lintable
2221
from ansiblelint.rules import AnsibleLintRule, TransformMixin
@@ -195,7 +194,7 @@ def matchtask(
195194
result.append(
196195
self.create_matcherror(
197196
message=str(exc),
198-
lineno=_get_error_line(task, path),
197+
lineno=task.get_error_line(path),
199198
filename=file,
200199
tag=f"{self.id}[invalid]",
201200
),
@@ -214,7 +213,7 @@ def matchtask(
214213
value=v,
215214
reformatted=reformatted,
216215
),
217-
lineno=_get_error_line(task, path),
216+
lineno=task.get_error_line(path),
218217
details=details,
219218
filename=file,
220219
tag=f"{self.id}[{tag}]",
@@ -233,12 +232,13 @@ def matchtask(
233232

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

240238
if str(file.kind) == "vars":
241239
data = parse_yaml_from_file(str(file.path))
240+
if not isinstance(data, dict):
241+
return results
242242
for key, v, _path in nested_items_path(data):
243243
if isinstance(v, AnsibleUnicode):
244244
reformatted, details, tag = self.check_whitespace(
@@ -406,7 +406,7 @@ def uncook(value: str, *, implicit: bool = False) -> str:
406406
except jinja2.exceptions.TemplateSyntaxError as exc:
407407
return "", str(exc.message), "invalid"
408408
# pylint: disable=c-extension-no-member
409-
except (NotImplementedError, black.parsing.InvalidInput) as exc:
409+
except (NotImplementedError, ValueError) as exc:
410410
# black is not able to recognize all valid jinja2 templates, so we
411411
# just ignore InvalidInput errors.
412412
# NotImplementedError is raised internally for expressions with
@@ -898,17 +898,3 @@ def _do_template(*args, **kwargs): # type: ignore[no-untyped-def] # Templar.do_
898898
with mock.patch.object(Templar, "do_template", _do_template):
899899
results = Runner(lintable, rules=collection).run()
900900
assert len(results) == 0
901-
902-
903-
def _get_error_line(task: dict[str, Any], path: list[str | int]) -> int:
904-
"""Return error line number."""
905-
line = task[LINE_NUMBER_KEY]
906-
ctx = task
907-
for _ in path:
908-
ctx = ctx[_]
909-
if LINE_NUMBER_KEY in ctx:
910-
line = ctx[LINE_NUMBER_KEY]
911-
if not isinstance(line, int):
912-
msg = "Line number is not an integer"
913-
raise TypeError(msg)
914-
return line

src/ansiblelint/rules/role_name.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,17 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
105105
msg = "Role dependency has unexpected type."
106106
raise TypeError(msg)
107107
if "/" in role_name:
108+
lineno = 1
109+
if hasattr(role_name, "ansible_pos"):
110+
lineno = role_name.ansible_pos[ # pyright: ignore[reportAttributeAccessIssue]
111+
1
112+
]
113+
108114
result.append(
109115
self.create_matcherror(
110116
f"Avoid using paths when importing roles. ({role_name})",
111117
filename=file,
112-
lineno=role_name.ansible_pos[1],
118+
lineno=lineno,
113119
tag=f"{self.id}[path]",
114120
),
115121
)
@@ -165,7 +171,7 @@ def matchyaml(self, file: Lintable) -> list[MatchError]:
165171
def _infer_role_name(meta: Path, default: str) -> str:
166172
if meta.is_file():
167173
meta_data = parse_yaml_from_file(str(meta))
168-
if meta_data:
174+
if meta_data and isinstance(meta_data, dict):
169175
try:
170176
return str(meta_data["galaxy_info"]["role_name"])
171177
except (KeyError, TypeError):

src/ansiblelint/utils.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from __future__ import annotations
2525

2626
import ast
27+
import collections.abc
2728
import contextlib
2829
import inspect
2930
import logging
@@ -306,7 +307,7 @@ def include_children(
306307
v = v["file"]
307308

308309
# we cannot really parse any jinja2 in includes, so we ignore them
309-
if not v or "{{" in v:
310+
if not v or not isinstance(v, str) or "{{" in v:
310311
return []
311312

312313
# handle include: filename.yml tags=blah
@@ -877,6 +878,31 @@ def __iter__(self) -> Iterator[str]:
877878
"""Provide support for 'key in task'."""
878879
yield from (f for f in self.normalized_task)
879880

881+
def get_error_line(self, path: list[str | int]) -> int:
882+
"""Return error line number."""
883+
ctx = self.normalized_task
884+
line = self.normalized_task[LINE_NUMBER_KEY]
885+
for _ in path:
886+
if (
887+
isinstance(ctx, collections.abc.Container) and _ in ctx
888+
): # isinstance(ctx, collections.abc.Container) and
889+
value = ctx.get( # pyright: ignore[reportAttributeAccessIssue]
890+
_ # pyright: ignore[reportArgumentType]
891+
)
892+
if isinstance(value, dict):
893+
ctx = value
894+
if (
895+
isinstance(ctx, collections.abc.Container)
896+
and LINE_NUMBER_KEY in ctx
897+
):
898+
line = ctx[LINE_NUMBER_KEY] # pyright: ignore[reportIndexIssue]
899+
# else:
900+
# break
901+
if not isinstance(line, int):
902+
msg = "Line number is not an integer"
903+
raise TypeError(msg)
904+
return line
905+
880906

881907
def task_in_list(
882908
data: AnsibleBaseYAMLObject,

src/ansiblelint/yaml_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,15 +244,15 @@ def _nested_items_path(
244244
"""
245245
# we have to cast each convert_to_tuples assignment or mypy complains
246246
# that both assignments (for dict and list) do not have the same type
247-
convert_to_tuples_type = Callable[[], Iterator[tuple[str | int, Any]]]
247+
# convert_to_tuples_type = Callable[[], Iterator[tuple[str | int, Any]]]
248248
if isinstance(data_collection, dict):
249249
convert_data_collection_to_tuples = cast(
250-
convert_to_tuples_type,
250+
Callable[[], Iterator[tuple[str | int, Any]]],
251251
functools.partial(data_collection.items),
252252
)
253253
elif isinstance(data_collection, list):
254254
convert_data_collection_to_tuples = cast(
255-
convert_to_tuples_type,
255+
Callable[[], Iterator[tuple[str | int, Any]]],
256256
functools.partial(enumerate, data_collection),
257257
)
258258
else:

test/test_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@
4545
from _pytest.capture import CaptureFixture
4646
from _pytest.logging import LogCaptureFixture
4747
from _pytest.monkeypatch import MonkeyPatch
48+
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject
4849

4950
from ansiblelint.rules import RulesCollection
5051

51-
5252
runtime = Runtime(require_module=True)
5353

5454

@@ -237,7 +237,7 @@ def test_extract_from_list_recursive() -> None:
237237
block = {
238238
"block": [{"block": [{"name": "hello", "command": "whoami"}]}],
239239
}
240-
blocks = [block]
240+
blocks: AnsibleBaseYAMLObject = [block]
241241

242242
test_list = utils.extract_from_list(blocks, ["block"])
243243
assert list(block["block"]) == test_list

test/test_yaml_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def test_tasks_in_list_empty_file(empty_lintable: Lintable) -> None:
3838
assert empty_lintable.path
3939
res = list(
4040
task_in_list(
41-
data=empty_lintable,
41+
data=empty_lintable.data,
4242
file=empty_lintable,
4343
kind=empty_lintable.kind,
4444
),

0 commit comments

Comments
 (0)