eliminate None template nodes in _flatten_nodes (#85676)

* defers value or concat choice until Nones are gone
* fixes None -> empty string for TemplateModule cases
* add tests
This commit is contained in:
Matt Davis 2025-08-15 15:13:10 -07:00 committed by GitHub
parent 603dd2d793
commit 5345ac9911
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 12 additions and 2 deletions

View File

@ -753,7 +753,7 @@ class AnsibleEnvironment(SandboxedEnvironment):
except MarkerError as ex:
return ex.source # return the first Marker encountered
return ''.join([to_text(v) for v in node_list if v is not None]) # skip concat on `None`-valued nodes to avoid literal "None" in template results
return ''.join([to_text(v) for v in node_list])
@staticmethod
def _access_const(const_template: t.LiteralString) -> t.Any:
@ -891,6 +891,8 @@ def _flatten_nodes(nodes: t.Iterable[t.Any]) -> t.Iterable[t.Any]:
else:
if type(node) is TemplateModule: # pylint: disable=unidiomatic-typecheck
yield from _flatten_nodes(node._body_stream)
elif node is None:
continue # avoid yielding `None`-valued nodes to avoid literal "None" in stringified template results
else:
yield node

View File

@ -27,6 +27,7 @@ import typing as t
import pytest_mock
from jinja2.runtime import Context
from jinja2.loaders import DictLoader
import unittest
@ -1085,9 +1086,16 @@ def test_marker_from_test_plugin() -> None:
("{% if False %}{% endif %}", None), # concat sees one node, NoneType result is preserved
("{{''}}{% if False %}{% endif %}", ""), # multiple blocks with an embedded None result, concat is in play, the result is an empty string
("hey {{ none }}", "hey "), # composite template, the result is an empty string
("{% import 'importme' as imported %}{{ imported }}", "imported template result"),
))
def test_none_concat(template: str, expected: object) -> None:
assert TemplateEngine().template(TRUST.tag(template)) == expected
"""Validate that None values are omitted from composite template concat."""
te = TemplateEngine()
# set up an importable template to exercise TemplateModule code paths
te.environment.loader = DictLoader(dict(importme=TRUST.tag("{{ none }}{{ 'imported template result' }}{{ none }}")))
assert te.template(TRUST.tag(template)) == expected
def test_filter_generator() -> None: