From 5e0fd1201c315ed27ac6f232257e3af3481d8df4 Mon Sep 17 00:00:00 2001 From: Christoph Fiehe Date: Wed, 18 Feb 2026 07:08:49 +0100 Subject: [PATCH] ModuleHelper: ensure compatibility with `ModuleTestCase` (#11488) * ModuleHelper: ensure compatibility with `ModuleTestCase`. This change allows to configure the `module_fails_on_exception` decorator by passing a tuple of exception types that should not be handled by the decorator itself. In the context of `ModuleTestCase`, use `(AnsibleExitJson, AnsibleFailJson)` to let them pass through the decorator without modification. Signed-off-by: Fiehe Christoph * Another approach allowing user-defined exception types to pass through the decorator. When the decorator should have no arguments at all, we must hard code the name of the attribute that is looked up on self. Signed-off-by: Fiehe Christoph * Approach that removes decorator parametrization and relies on an object/class variable named `unhandled_exceptions`. Signed-off-by: Fiehe Christoph * context manager implemented that allows to pass through some exception types Signed-off-by: Fiehe Christoph * Update changelogs/fragments/11488-mh-ensure-compatibiliy-with-module-tests.yml Co-authored-by: Felix Fontein * Exception placeholder added Signed-off-by: Fiehe Christoph --------- Signed-off-by: Fiehe Christoph Co-authored-by: Fiehe Christoph Co-authored-by: Felix Fontein --- ...-ensure-compatibiliy-with-module-tests.yml | 2 ++ plugins/module_utils/mh/deco.py | 22 ++++++++++++++++++- plugins/module_utils/mh/exceptions.py | 4 ++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 changelogs/fragments/11488-mh-ensure-compatibiliy-with-module-tests.yml diff --git a/changelogs/fragments/11488-mh-ensure-compatibiliy-with-module-tests.yml b/changelogs/fragments/11488-mh-ensure-compatibiliy-with-module-tests.yml new file mode 100644 index 0000000000..25312f77d5 --- /dev/null +++ b/changelogs/fragments/11488-mh-ensure-compatibiliy-with-module-tests.yml @@ -0,0 +1,2 @@ +minor_changes: + - ModuleHelper module utils - allow to ignore specific exceptions in ``module_fails_on_exception`` decorator (https://github.com/ansible-collections/community.general/pull/11488). diff --git a/plugins/module_utils/mh/deco.py b/plugins/module_utils/mh/deco.py index e05492b66a..207ccc38dd 100644 --- a/plugins/module_utils/mh/deco.py +++ b/plugins/module_utils/mh/deco.py @@ -6,9 +6,15 @@ from __future__ import annotations import traceback +from contextlib import contextmanager from functools import wraps -from ansible_collections.community.general.plugins.module_utils.mh.exceptions import ModuleHelperException +from ansible_collections.community.general.plugins.module_utils.mh.exceptions import ( + ModuleHelperException, + _UnhandledSentinel, +) + +_unhandled_exceptions: tuple[type[Exception], ...] = (_UnhandledSentinel,) def cause_changes(when=None): @@ -32,6 +38,17 @@ def cause_changes(when=None): return deco +@contextmanager +def no_handle_exceptions(*exceptions: type[Exception]): + global _unhandled_exceptions + current = _unhandled_exceptions + _unhandled_exceptions = tuple(exceptions) + try: + yield + finally: + _unhandled_exceptions = current + + def module_fails_on_exception(func): conflict_list = ("msg", "exception", "output", "vars", "changed") @@ -46,6 +63,9 @@ def module_fails_on_exception(func): try: func(self, *args, **kwargs) + except _unhandled_exceptions: + # re-raise exception without further processing + raise except ModuleHelperException as e: if e.update_output: self.update_output(e.update_output) diff --git a/plugins/module_utils/mh/exceptions.py b/plugins/module_utils/mh/exceptions.py index 612cfc1c3c..64c0d4b176 100644 --- a/plugins/module_utils/mh/exceptions.py +++ b/plugins/module_utils/mh/exceptions.py @@ -15,3 +15,7 @@ class ModuleHelperException(Exception): update_output = {} self.update_output: dict[str, t.Any] = update_output super().__init__(*args) + + +class _UnhandledSentinel(Exception): + pass