diff --git a/changelogs/fragments/jinja2_native-literal_eval-py310.yml b/changelogs/fragments/jinja2_native-literal_eval-py310.yml new file mode 100644 index 00000000000000..70a66af8e8c456 --- /dev/null +++ b/changelogs/fragments/jinja2_native-literal_eval-py310.yml @@ -0,0 +1,2 @@ +minor_changes: + - jinja2_native - keep same behavior on Python 3.10. diff --git a/lib/ansible/playbook/conditional.py b/lib/ansible/playbook/conditional.py index f3118a81b2935d..5a909e7c9eea61 100644 --- a/lib/ansible/playbook/conditional.py +++ b/lib/ansible/playbook/conditional.py @@ -184,12 +184,7 @@ def generic_visit(self, node, inside_call=False, inside_yield=False): # NOTE The spaces around True and False are intentional to short-circuit literal_eval for # jinja2_native=False and avoid its expensive calls. presented = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % conditional - # NOTE Convert the result to text to account for both native and non-native jinja. - # NOTE The templated result of `presented` is string on native jinja as well prior to Python 3.10. - # ast.literal_eval on Python 3.10 removes leading whitespaces so " True " becomes bool True - # as opposed to Python 3.9 and lower where the same would result in IndentationError and - # string " True " would be returned by Templar. - val = to_text(templar.template(presented, disable_lookups=disable_lookups)).strip() + val = templar.template(presented, disable_lookups=disable_lookups).strip() if val == "True": return True elif val == "False": diff --git a/lib/ansible/template/native_helpers.py b/lib/ansible/template/native_helpers.py index 63d6bdc6712484..c75d356b38a20b 100644 --- a/lib/ansible/template/native_helpers.py +++ b/lib/ansible/template/native_helpers.py @@ -140,6 +140,11 @@ def ansible_native_concat(nodes): out = ''.join([to_text(_fail_on_undefined(v)) for v in chain(head, nodes)]) try: - return ast.literal_eval(out) + return ast.literal_eval( + # In Python 3.10+ ast.literal_eval removes leading spaces/tabs + # from the given string. For backwards compatibility we need to + # parse the string ourselves without removing leading spaces/tabs. + ast.parse(out, mode='eval') + ) except (ValueError, SyntaxError, MemoryError): return out