diff --git a/changelogs/fragments/allow-fail-json-msg-to-be-positional.yaml b/changelogs/fragments/allow-fail-json-msg-to-be-positional.yaml new file mode 100644 index 00000000000..8b58a4abd42 --- /dev/null +++ b/changelogs/fragments/allow-fail-json-msg-to-be-positional.yaml @@ -0,0 +1,5 @@ +minor_features: + - `AnsibleModule.fail_json()` has always required that a message be passed + in which informs the end user why the module failed. In the past this + message had to be passed as the `msg` keyword argument but it can now be + passed as the first positional argument instead. diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py index 4e6f57f81af..185b0ebe072 100644 --- a/lib/ansible/module_utils/basic.py +++ b/lib/ansible/module_utils/basic.py @@ -2041,12 +2041,11 @@ class AnsibleModule(object): self._return_formatted(kwargs) sys.exit(0) - def fail_json(self, **kwargs): + def fail_json(self, msg, **kwargs): ''' return from the module, with an error message ''' - if 'msg' not in kwargs: - raise AssertionError("implementation error -- msg to explain the error is required") kwargs['failed'] = True + kwargs['msg'] = msg # Add traceback if debug or high verbosity and it is missing # NOTE: Badly named as exception, it really always has been a traceback diff --git a/test/units/module_utils/basic/test_exit_json.py b/test/units/module_utils/basic/test_exit_json.py index cf63b98768a..65fc5b88428 100644 --- a/test/units/module_utils/basic/test_exit_json.py +++ b/test/units/module_utils/basic/test_exit_json.py @@ -55,11 +55,23 @@ class TestAnsibleModuleExitJson: expected['failed'] = True assert return_val == expected + @pytest.mark.parametrize('stdin', [{}], indirect=['stdin']) + def test_fail_json_msg_positional(self, am, capfd): + with pytest.raises(SystemExit) as ctx: + am.fail_json('This is the msg') + assert ctx.value.code == 1 + + out, err = capfd.readouterr() + return_val = json.loads(out) + # Fail_json should add failed=True + assert return_val == {'msg': 'This is the msg', 'failed': True, + 'invocation': EMPTY_INVOCATION} + @pytest.mark.parametrize('stdin', [{}], indirect=['stdin']) def test_fail_json_no_msg(self, am): - with pytest.raises(AssertionError) as ctx: + with pytest.raises(TypeError) as ctx: am.fail_json() - assert ctx.value.args[0] == "implementation error -- msg to explain the error is required" + assert ctx.value.args[0] == "fail_json() missing 1 required positional argument: 'msg'" class TestAnsibleModuleExitValuesRemoved: