From be7b4f43c85366500f699c02dbfb6ac7a6afdaa5 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 12 Aug 2025 00:31:11 +0200 Subject: [PATCH 1/5] Add example of unintentional None to 2.19 porting guide. --- .../porting_guide_core_2.19.rst | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst b/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst index 396b0a6f449..8a7aa2abf07 100644 --- a/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst +++ b/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst @@ -407,6 +407,53 @@ The result of the corrected template remains a list: } +Example - unintentional ``None`` +"""""""""""""""""""""""""""""""" + +If a part of a template evaluated to ``None``, it was implicitly converted to an empty string in previous versions of ansible-core. +This can now result in the template evaluating to the *value* ``None``, or can result in the templated string containing the character sequence ``None``. + +The following example shows two cases where this happens: + +.. code-block:: yaml+jinja + + - set_fact: + # If 'foo' is not defined, the else branch basically evaluates to None. + # So value_none will not be an empty string, but None: + value_none: |- + {% if foo is defined %}foo is defined{% endif %} + + # The expression 'items.append(x)' evaluates to None, so the resulting string + # will be "NoneNoneNone['a', 'b', 'c']" instead of the array ['a', 'b', 'c']: + string_with_none: |- + {% set items = [] %} + {% for x in ['a', 'b', 'c'] %} + {{- items.append(x) -}} + {% endfor %} + {{ items }} + +These examples can be fixed as follows: + +.. code-block:: yaml+jinja + + - set_fact: + # Explicitly return an empty string in the 'else' branch. + # The value is always a string: either "foo is defined" or "". + value_none: |- + {% if foo is defined %}foo is defined{% else %}{{ "" }}{% endif %} + + # Use {% set _ = expression %} instead of {{ expression }} eats the + # return value. The template evalutes to the array ['a', 'b', 'c']. + string_with_none: |- + {% set items = [] %} + {% for x in ['a', 'b', 'c'] %} + {%- set _ = items.append(x) -%} + {% endfor %} + {{ items }} + +These adjustments also work fine with older ansible-core versions. + + Lazy templating ^^^^^^^^^^^^^^^ From 04255d4688817c8256cf67c906fce5c13e74cb3a Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sun, 17 Aug 2025 22:29:23 +0200 Subject: [PATCH 2/5] Rewrite into two sections. --- .../porting_guide_core_2.19.rst | 57 +++++++++++++------ 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst b/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst index 8a7aa2abf07..882819b3a79 100644 --- a/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst +++ b/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst @@ -407,13 +407,13 @@ The result of the corrected template remains a list: } -Example - unintentional ``None`` -"""""""""""""""""""""""""""""""" +Example - unintentional ``None`` result +""""""""""""""""""""""""""""""""""""""" -If a part of a template evaluated to ``None``, it was implicitly converted to an empty string in previous versions of ansible-core. -This can now result in the template evaluating to the *value* ``None``, or can result in the templated string containing the character sequence ``None``. +If a template evaluated to ``None``, it was implicitly converted to an empty string in previous versions of ansible-core. +This can now result in the template evaluating to the *value* ``None``. -The following example shows two cases where this happens: +The following example shows a case where this happens: .. code-block:: yaml+jinja @@ -423,35 +423,56 @@ The following example shows two cases where this happens: value_none: |- {% if foo is defined %}foo is defined{% endif %} - # The expression 'items.append(x)' evaluates to None, so the resulting string - # will be "NoneNoneNone['a', 'b', 'c']" instead of the array ['a', 'b', 'c']: - string_with_none: |- +This example can be fixed as follows: + +.. code-block:: yaml+jinja + + - set_fact: + # Explicitly return an empty string in the 'else' branch. + # The value is always a string: either "foo is defined" or "". + value_none: |- + {% if foo is defined %}foo is defined{% else %}{{ "" }}{% endif %} + +This adjustment also work fine with older ansible-core versions. + + +Example - unintentional ``None`` preventing native output +""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +If a part of a template evaluated to ``None``, it was implicitly converted to an empty string in previous versions of ansible-core. +When using a template to compute a native value, like a list, you have to make sure to not accidentally insert ``None`` values before or after the value. + +The following example shows a case where this happens: + +.. code-block:: yaml+jinja + + - set_fact: + # The expression 'items.append(x)' evaluates to None, so the template evalutes to + # three None's followed by the list ['a', 'b', 'c']. + # In older ansible-core versions, the way evaluation was done this still resulted in a list. + # Now, it results in a stringified version of the list instead: "['a', 'b', 'c']". + evaluate_to_string_instead_of_list: |- {% set items = [] %} {% for x in ['a', 'b', 'c'] %} {{- items.append(x) -}} {% endfor %} {{ items }} -These examples can be fixed as follows: +This example can be fixed as follows: .. code-block:: yaml+jinja - set_fact: - # Explicitly return an empty string in the 'else' branch. - # The value is always a string: either "foo is defined" or "". - value_none: |- - {% if foo is defined %}foo is defined{% else %}{{ "" }}{% endif %} - - # Use {% set _ = expression %} instead of {{ expression }} eats the - # return value. The template evalutes to the array ['a', 'b', 'c']. - string_with_none: |- + # Using {% set _ = expression %} instead of {{ expression }} eats the + # return value. The template evalutes to the array ['a', 'b', 'c']: + evaluate_to_string_instead_of_list: |- {% set items = [] %} {% for x in ['a', 'b', 'c'] %} {%- set _ = items.append(x) -%} {% endfor %} {{ items }} -These adjustments also work fine with older ansible-core versions. +This adjustment also work fine with older ansible-core versions. Lazy templating From 3d8ad3310eefbc99caeb2add17754dbc07e34c36 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Mon, 18 Aug 2025 21:22:29 +0200 Subject: [PATCH 3/5] Improve formulations as suggested by samccann. Co-authored-by: Sandra McCann --- docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst b/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst index 882819b3a79..d0e61378f72 100644 --- a/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst +++ b/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst @@ -433,7 +433,7 @@ This example can be fixed as follows: value_none: |- {% if foo is defined %}foo is defined{% else %}{{ "" }}{% endif %} -This adjustment also work fine with older ansible-core versions. +This adjustment is backward-compable with older ansible-core versions. Example - unintentional ``None`` preventing native output @@ -472,7 +472,7 @@ This example can be fixed as follows: {% endfor %} {{ items }} -This adjustment also work fine with older ansible-core versions. +This adjustment is backward-compable with older ansible-core versions. Lazy templating From 163eb58316c9575bcb5b46290ecfaf8df22abe9f Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Wed, 20 Aug 2025 19:22:07 +0200 Subject: [PATCH 4/5] Remove no longer appropriate section. --- .../porting_guide_core_2.19.rst | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst b/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst index d0e61378f72..2566d424966 100644 --- a/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst +++ b/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst @@ -436,45 +436,6 @@ This example can be fixed as follows: This adjustment is backward-compable with older ansible-core versions. -Example - unintentional ``None`` preventing native output -""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -If a part of a template evaluated to ``None``, it was implicitly converted to an empty string in previous versions of ansible-core. -When using a template to compute a native value, like a list, you have to make sure to not accidentally insert ``None`` values before or after the value. - -The following example shows a case where this happens: - -.. code-block:: yaml+jinja - - - set_fact: - # The expression 'items.append(x)' evaluates to None, so the template evalutes to - # three None's followed by the list ['a', 'b', 'c']. - # In older ansible-core versions, the way evaluation was done this still resulted in a list. - # Now, it results in a stringified version of the list instead: "['a', 'b', 'c']". - evaluate_to_string_instead_of_list: |- - {% set items = [] %} - {% for x in ['a', 'b', 'c'] %} - {{- items.append(x) -}} - {% endfor %} - {{ items }} - -This example can be fixed as follows: - -.. code-block:: yaml+jinja - - - set_fact: - # Using {% set _ = expression %} instead of {{ expression }} eats the - # return value. The template evalutes to the array ['a', 'b', 'c']: - evaluate_to_string_instead_of_list: |- - {% set items = [] %} - {% for x in ['a', 'b', 'c'] %} - {%- set _ = items.append(x) -%} - {% endfor %} - {{ items }} - -This adjustment is backward-compable with older ansible-core versions. - - Lazy templating ^^^^^^^^^^^^^^^ From dbe43f4f2e230bd7eebe0ae5be76785333fe2369 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Wed, 20 Aug 2025 19:22:14 +0200 Subject: [PATCH 5/5] Fix typo and add note. --- .../rst/porting_guides/porting_guide_core_2.19.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst b/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst index 2566d424966..32c2fd1dffa 100644 --- a/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst +++ b/docs/docsite/rst/porting_guides/porting_guide_core_2.19.rst @@ -433,7 +433,13 @@ This example can be fixed as follows: value_none: |- {% if foo is defined %}foo is defined{% else %}{{ "" }}{% endif %} -This adjustment is backward-compable with older ansible-core versions. +This adjustment is backward-compatible with older ansible-core versions. + +.. note:: + Since ansible-core 2.19.1, module options of type string accept ``None`` and convert it + to an empty string. Before ansible-core 2.18, passing ``None`` to such options resulted + in an error. This means that in most cases, expressions in roles and playbooks do not need + to be adjusted because of unintentional ``None`` results. Lazy templating