Skip to content

Commit a38b64b

Browse files
authored
Avoid import errors from filters when running in nodeps mode (#4651)
1 parent ccc4a18 commit a38b64b

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)