diff --git a/changelogs/fragments/4791-cmd-runner-callable.yaml b/changelogs/fragments/4791-cmd-runner-callable.yaml new file mode 100644 index 00000000000..142ad90f83e --- /dev/null +++ b/changelogs/fragments/4791-cmd-runner-callable.yaml @@ -0,0 +1,2 @@ +minor_changes: + - cmd_runner module utils - add ``__call__`` method to invoke context (https://github.com/ansible-collections/community.general/pull/4791). diff --git a/plugins/module_utils/cmd_runner.py b/plugins/module_utils/cmd_runner.py index 7d6a690224b..6aec7b1a90b 100644 --- a/plugins/module_utils/cmd_runner.py +++ b/plugins/module_utils/cmd_runner.py @@ -197,7 +197,7 @@ def __init__(self, module, command, arg_formats=None, default_args_order=(), if mod_param_name not in self.arg_formats: self.arg_formats[mod_param_name] = _Format.as_default_type(spec['type'], mod_param_name) - def context(self, args_order=None, output_process=None, ignore_value_none=True, check_mode_skip=False, check_mode_return=None, **kwargs): + def __call__(self, args_order=None, output_process=None, ignore_value_none=True, check_mode_skip=False, check_mode_return=None, **kwargs): if output_process is None: output_process = _process_as_is if args_order is None: @@ -216,6 +216,9 @@ def context(self, args_order=None, output_process=None, ignore_value_none=True, def has_arg_format(self, arg): return arg in self.arg_formats + # not decided whether to keep it or not, but if deprecating it will happen in a farther future. + context = __call__ + class _CmdRunnerContext(object): def __init__(self, runner, args_order, output_process, ignore_value_none, check_mode_skip, check_mode_return, **kwargs): diff --git a/plugins/modules/system/xfconf.py b/plugins/modules/system/xfconf.py index c2811e3ccb8..9d6521cced5 100644 --- a/plugins/modules/system/xfconf.py +++ b/plugins/modules/system/xfconf.py @@ -209,11 +209,11 @@ def process_command_output(self, rc, out, err): return result def _get(self): - with self.runner.context('channel property', output_process=self.process_command_output) as ctx: + with self.runner('channel property', output_process=self.process_command_output) as ctx: return ctx.run() def state_absent(self): - with self.runner.context('channel property reset', check_mode_skip=True) as ctx: + with self.runner('channel property reset', check_mode_skip=True) as ctx: ctx.run(reset=True) self.vars.value = None @@ -239,7 +239,7 @@ def state_present(self): isinstance(self.vars.previous_value, list) or \ values_len > 1 - with self.runner.context('channel property create force_array values_and_types', check_mode_skip=True) as ctx: + with self.runner('channel property create force_array values_and_types', check_mode_skip=True) as ctx: ctx.run(create=True, force_array=self.vars.is_array, values_and_types=(self.vars.value, value_type)) if not self.vars.is_array: diff --git a/tests/unit/plugins/module_utils/test_cmd_runner.py b/tests/unit/plugins/module_utils/test_cmd_runner.py index b5145ec8151..f28c8904138 100644 --- a/tests/unit/plugins/module_utils/test_cmd_runner.py +++ b/tests/unit/plugins/module_utils/test_cmd_runner.py @@ -246,7 +246,7 @@ def test_arg_format(func, fmt_opt, value, expected): @pytest.mark.parametrize('runner_input, cmd_execution, expected', (TC_RUNNER[tc] for tc in TC_RUNNER_IDS), ids=TC_RUNNER_IDS) -def test_runner(runner_input, cmd_execution, expected): +def test_runner_context(runner_input, cmd_execution, expected): arg_spec = {} params = {} arg_formats = {} @@ -304,3 +304,66 @@ def _assert_run(runner_input, cmd_execution, expected, ctx, results): with runner.context(**runner_input['runner_ctx_args']) as ctx: results = ctx.run(**cmd_execution['runner_ctx_run_args']) _assert_run(runner_input, cmd_execution, expected, ctx, results) + + +@pytest.mark.parametrize('runner_input, cmd_execution, expected', + (TC_RUNNER[tc] for tc in TC_RUNNER_IDS), + ids=TC_RUNNER_IDS) +def test_runner_callable(runner_input, cmd_execution, expected): + arg_spec = {} + params = {} + arg_formats = {} + for k, v in runner_input['args_bundle'].items(): + try: + arg_spec[k] = {'type': v['type']} + except KeyError: + pass + try: + params[k] = v['value'] + except KeyError: + pass + try: + arg_formats[k] = v['fmt_func'](v['fmt_arg']) + except KeyError: + pass + + orig_results = tuple(cmd_execution[x] for x in ('rc', 'out', 'err')) + + print("arg_spec={0}\nparams={1}\narg_formats={2}\n".format( + arg_spec, + params, + arg_formats, + )) + + module = MagicMock() + type(module).argument_spec = PropertyMock(return_value=arg_spec) + type(module).params = PropertyMock(return_value=params) + module.get_bin_path.return_value = '/mock/bin/testing' + module.run_command.return_value = orig_results + + runner = CmdRunner( + module=module, + command="testing", + arg_formats=arg_formats, + **runner_input['runner_init_args'] + ) + + def _assert_run_info(actual, expected): + reduced = dict((k, actual[k]) for k in expected.keys()) + assert reduced == expected, "{0}".format(reduced) + + def _assert_run(runner_input, cmd_execution, expected, ctx, results): + _assert_run_info(ctx.run_info, expected['run_info']) + assert results == expected.get('results', orig_results) + + exc = expected.get("exc") + if exc: + with pytest.raises(exc): + with runner(**runner_input['runner_ctx_args']) as ctx: + results = ctx.run(**cmd_execution['runner_ctx_run_args']) + _assert_run(runner_input, cmd_execution, expected, ctx, results) + + else: + with runner(**runner_input['runner_ctx_args']) as ctx: + results = ctx.run(**cmd_execution['runner_ctx_run_args']) + _assert_run(runner_input, cmd_execution, expected, ctx, results)