diff --git a/changelogs/fragments/normalize_booleanization.yml b/changelogs/fragments/normalize_booleanization.yml new file mode 100644 index 00000000000..b29d640b9c8 --- /dev/null +++ b/changelogs/fragments/normalize_booleanization.yml @@ -0,0 +1,2 @@ +minor_changes: + - bool filter now has new strategy option and by default equates YAML 1.1 booleanization, before it just aproximated it. diff --git a/lib/ansible/plugins/filter/bool.yml b/lib/ansible/plugins/filter/bool.yml index beb8b8ddb1f..2f6262d5a2f 100644 --- a/lib/ansible/plugins/filter/bool.yml +++ b/lib/ansible/plugins/filter/bool.yml @@ -10,17 +10,31 @@ DOCUMENTATION: description: Data to cast. type: raw required: true + strategy: + description: Evaluation engine to use while evaluating boolean. + default: yaml + choices: + yaml: Use YAML 1.1 allowed booleans + python: Pass through the Python C(bool) function + truthy: Just let Python evaluate in an C(if) statement + version_added: '2.18' + notes: + - Before adding the 'strategy' option, evaluation approximated the 'yaml' strategy. + - if the value is already a boolean or C(None), this filter returns the original value. EXAMPLES: | # in vars vars: isbool: "{{ (a == b) | bool }} " - otherbool: "{{ anothervar | bool }} " + otherbool: "{{ anothervar | bool('yaml') }} " + yetanotherbool: "{{ pythonbool | bool(strategy='python') }} " # in a task ... - when: some_string_value | bool + when: + - some_string_value | bool + - other != notsame RETURN: _value: diff --git a/lib/ansible/plugins/filter/core.py b/lib/ansible/plugins/filter/core.py index 79f78d728f3..bcb30d38b1a 100644 --- a/lib/ansible/plugins/filter/core.py +++ b/lib/ansible/plugins/filter/core.py @@ -28,6 +28,7 @@ from ansible.module_utils.six import string_types, integer_types, reraise, text_ from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text from ansible.module_utils.common.collections import is_sequence from ansible.module_utils.common.yaml import yaml_load, yaml_load_all +from ansible.module_utils.parsing.convert_bool import boolean from ansible.parsing.ajson import AnsibleJSONEncoder from ansible.parsing.yaml.dumper import AnsibleDumper from ansible.template import recursive_check_defined @@ -80,14 +81,28 @@ def to_nice_json(a, indent=4, sort_keys=True, *args, **kw): return to_json(a, indent=indent, sort_keys=sort_keys, separators=(',', ': '), *args, **kw) -def to_bool(a): +def to_bool(a, strategy='yaml'): ''' return a bool for the arg ''' + + # nothing to do if a is None or isinstance(a, bool): return a - if isinstance(a, string_types): - a = a.lower() - if a in ('yes', 'on', '1', 'true', 1): - return True + + valid_choices = ('yaml', 'python', 'truthy') + if strategy not in valid_choices: + raise AnsibleFilterError(f"Invalid strategy provided ({strategy}), valid choices are: {', '.join(valid_choices)}") + + try: + if strategy == 'yaml': + # make it lower case for easier matching + return boolean(a) + if strategy == 'python': + return bool(a) + if strategy == 'truthy': + if a: + return True + except TypeError as e: + raise AnsibleFilterTypeError(f"Could not convert to boolean using strategy {strategy}: {e}", orig_exc=e) return False diff --git a/test/integration/targets/filter_core/tasks/main.yml b/test/integration/targets/filter_core/tasks/main.yml index 8b325a93279..e078032e46a 100644 --- a/test/integration/targets/filter_core/tasks/main.yml +++ b/test/integration/targets/filter_core/tasks/main.yml @@ -301,14 +301,28 @@ - no_match_is_none_inline - failure is failed -- name: Verify to_bool - assert: - that: - - 'None|bool == None' - - 'False|bool == False' - - '"TrUe"|bool == True' - - '"FalSe"|bool == False' - - '7|bool == False' +- name: to_bool + tags: + - to_bool + block: + - name: Verify to_bool, using default 'yaml' strategy + assert: + that: + - 'None|bool == None' + - 'False|bool == False' + - '"TrUe"|bool == True' + - '"FalSe"|bool == False' + + - name: non valid bool should fail + set_fact: + failbool: '{{7|bool}}' + register: should_fail + ignore_errors: true + + - name: ensure we failed + assert: + that: + - should_fail is failed - name: Verify to_datetime assert: