Skip to content

Commit 10363a8

Browse files
committed
Avoid import errors from filters with nodeps mode
This should prevent failures when running in special nodeps mode, like the one used by galaxy-importer. Unblocks: ansible-collections/community.molecule#17 Related: AAP-46604
1 parent ccc4a18 commit 10363a8

File tree

4 files changed

+78
-5
lines changed

4 files changed

+78
-5
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Sample filter that should raise an ImportError when used."""
2+
3+
from collections.abc import Callable
4+
from typing import Any
5+
6+
# pylint: skip-file
7+
8+
DOCUMENTATION = """
9+
name: from_yaml
10+
description:
11+
- This callback just adds total play duration to the play stats.
12+
"""
13+
14+
15+
def filter_with_importerror(data: Any) -> dict[str, str]: # noqa: ARG001
16+
"""Sample filter.
17+
18+
:return: dict
19+
"""
20+
import a_module_that_does_not_exist # type: ignore[reportMissingImports] # noqa: F401
21+
22+
return {}
23+
24+
25+
class FilterModule:
26+
"""Core filter plugins."""
27+
28+
def filters(self) -> dict[str, Callable[..., dict[str, str]]]:
29+
"""Return implemented filters."""
30+
return {
31+
"filter_with_importerror": filter_with_importerror,
32+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
- name: Fixture
3+
hosts: localhost
4+
tasks:
5+
- name: Test
6+
ansible.builtin.debug:
7+
msg: "Some {{ 'foo' | filter_with_importerror }}"

src/ansiblelint/rules/jinja.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
import black
1515
import jinja2
1616
from ansible.errors import AnsibleError, AnsibleFilterError, AnsibleParserError
17+
from ansible_compat.config import ansible_version
1718
from jinja2.exceptions import TemplateSyntaxError
19+
from packaging.version import Version
1820

1921
from ansiblelint.errors import RuleMatchTransformMeta
2022
from ansiblelint.file_utils import Lintable
@@ -145,11 +147,15 @@ def matchtask(
145147
except AnsibleFilterError:
146148
bypass = True
147149
# ValueError RepresenterError
148-
except AnsibleError as exc:
150+
except (AnsibleError, ImportError) as exc:
149151
bypass = False
150-
orig_exc = (
151-
exc.orig_exc if getattr(exc, "orig_exc", None) else exc
152-
)
152+
orig_exc = exc
153+
if (
154+
isinstance(exc, AnsibleError)
155+
and hasattr(exc, "orig_exc")
156+
and exc.orig_exc
157+
):
158+
orig_exc = exc.orig_exc
153159
orig_exc_message = getattr(orig_exc, "message", str(orig_exc))
154160
match = self._ansible_error_re.match(
155161
getattr(orig_exc, "message", str(orig_exc)),
@@ -185,6 +191,12 @@ def matchtask(
185191
bypass = True
186192
else:
187193
bypass = False
194+
elif isinstance(exc, ImportError):
195+
if self.options and self.options.nodeps:
196+
msg = f"Ignored exception {exc} due to running with nodeps mode."
197+
_logger.debug(msg)
198+
continue
199+
bypass = False
188200
elif re.match(r"^lookup plugin (.*) not found$", exc.message):
189201
# lookup plugin 'template' not found
190202
bypass = True
@@ -905,3 +917,25 @@ def _do_template(*args, **kwargs): # type: ignore[no-untyped-def] # Templar.do_
905917
with mock.patch.object(Templar, "do_template", _do_template):
906918
results = Runner(lintable, rules=collection).run()
907919
assert len(results) == 0
920+
921+
@pytest.mark.parametrize(
922+
("nodeps", "expected_results"),
923+
(
924+
pytest.param(
925+
"0",
926+
0 if ansible_version() >= Version("2.19.0.dev0") else 1,
927+
id="normal",
928+
),
929+
pytest.param("1", 0, id="nodeps"),
930+
),
931+
)
932+
def test_filter_import_failure(
933+
nodeps: str, expected_results: int, monkeypatch: pytest.MonkeyPatch
934+
) -> None:
935+
"""Tests how we process import failures from within filters."""
936+
monkeypatch.setenv("ANSIBLE_LINT_NODEPS", nodeps)
937+
collection = RulesCollection()
938+
collection.register(JinjaRule())
939+
lintable = Lintable("examples/playbooks/test_filter_with_importerror.yml")
940+
results = Runner(lintable, rules=collection).run()
941+
assert len(results) == expected_results

src/ansiblelint/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ def template(
320320
)
321321
# Hack to skip the following exception when using to_json filter on a variable. # noqa: FIX004
322322
# I guess the filter doesn't like empty vars...
323-
except (AnsibleError, ValueError, RepresenterError):
323+
except (AnsibleError, ValueError, RepresenterError, ImportError):
324324
# templating failed, so just keep value as is.
325325
if fail_on_error:
326326
raise

0 commit comments

Comments
 (0)