Skip to content

Commit e1bd688

Browse files
Fail rocoto realize if XML contains unrendered expressions. (#805)
If any unrendered Jinja2 expressions remain in the Rocoto XML, report which values are needed and raise an error.
1 parent e6326c4 commit e1bd688

File tree

2 files changed

+27
-3
lines changed

2 files changed

+27
-3
lines changed

src/uwtools/rocoto.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@
1414
from time import sleep
1515
from typing import TYPE_CHECKING, Any
1616

17+
from jinja2 import Environment, StrictUndefined, meta
1718
from lxml import etree
1819
from lxml.builder import E # type: ignore[import-not-found]
1920
from lxml.etree import Element, SubElement, _Element
2021

2122
from uwtools.config.formats.yaml import YAMLConfig
23+
from uwtools.config.jinja2 import unrendered
2224
from uwtools.config.validator import validate_external as validate_yaml
23-
from uwtools.exceptions import UWConfigError, UWError
24-
from uwtools.logging import log
25+
from uwtools.exceptions import UWConfigError, UWConfigRealizeError, UWError
26+
from uwtools.logging import INDENT, log
2527
from uwtools.utils.file import readable, resource_path, writable
2628
from uwtools.utils.processing import run_shell_cmd
2729

@@ -58,6 +60,15 @@ def realize(config: YAMLConfig | Path | None, output_file: Path | None = None) -
5860
"""
5961
rxml = _RocotoXML(config)
6062
xml = str(rxml).strip()
63+
if unrendered(xml):
64+
log.error(xml)
65+
log.error("Value(s) needed to render this XML are:")
66+
for var in meta.find_undeclared_variables(
67+
Environment(undefined=StrictUndefined).parse(xml)
68+
):
69+
log.error("%s%s", INDENT, var)
70+
msg = "Rocoto XML could not be totally realized"
71+
raise UWConfigRealizeError(msg)
6172
if not validate_string(xml):
6273
msg = "Internal error: Invalid Rocoto XML"
6374
raise UWError(msg)

src/uwtools/tests/test_rocoto.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from uwtools import rocoto
1515
from uwtools.config.formats.yaml import YAMLConfig
16-
from uwtools.exceptions import UWConfigError, UWError
16+
from uwtools.exceptions import UWConfigError, UWConfigRealizeError, UWError
1717
from uwtools.tests.support import fixture_path, schema_validator
1818

1919
# Fixtures
@@ -88,6 +88,19 @@ def test_rocoto_realize__invalid_xml(assets):
8888
rocoto.realize(config=cfgfile, output_file=outfile)
8989

9090

91+
def test_rocoto_realize__unrendered_xml(assets, logged):
92+
cfgfile, outfile = assets
93+
template = "Hello {{ world }}."
94+
with (
95+
patch.object(rocoto, "_RocotoXML", return_value=template),
96+
raises(UWConfigRealizeError, match=r"Rocoto XML could not be totally realized"),
97+
):
98+
rocoto.realize(config=cfgfile, output_file=outfile)
99+
assert logged(template)
100+
assert logged("Value(s) needed to render this XML are:")
101+
assert logged("world")
102+
103+
91104
def test_rocoto_validate__file_fail(validation_assets):
92105
xml_file_bad, _, _, _ = validation_assets
93106
assert rocoto.validate_file(xml_file=xml_file_bad) is False

0 commit comments

Comments
 (0)