Skip to content

Commit 4aba490

Browse files
author
rileykk
committed
Add optional validator to jinja2 render function
1 parent 2575f86 commit 4aba490

File tree

2 files changed

+42
-7
lines changed

2 files changed

+42
-7
lines changed

Diff for: requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ flake8-import-order
44
h5py
55
Jinja2==3.1.5
66
jsonschema
7+
lxml
78
mgrs
89
numpy<2
910
pandas

Diff for: src/opera/util/render_jinja2.py

+41-7
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,20 @@
1212
"""
1313

1414
import json
15-
import numpy as np
1615
import os
1716
import re
17+
18+
from functools import partial
19+
1820
import jinja2
21+
import yaml
22+
23+
import numpy as np
1924

25+
from lxml import etree
2026
from opera.util.error_codes import ErrorCode
2127
from opera.util.logger import PgeLogger
2228

23-
2429
XML_TYPES = {
2530
str: 'string',
2631
int: 'int',
@@ -87,18 +92,18 @@ def _log_message(undef):
8792
class LoggingUndefined(jinja2.Undefined):
8893
"""Override the default behavior which can raise an exception"""
8994

90-
def _fail_with_undefined_error(self, *args, **kwargs): # pragma no cover
95+
def _fail_with_undefined_error(self, *args, **kwargs): # pragma no cover
9196
_log_message(self)
9297

9398
def __str__(self):
9499
_log_message(self)
95100
return UNDEFINED_ERROR
96101

97-
def __iter__(self): # pragma no cover
102+
def __iter__(self): # pragma no cover
98103
_log_message(self)
99104
return super().__iter__()
100105

101-
def __bool__(self): # pragma no cover
106+
def __bool__(self): # pragma no cover
102107
_log_message(self)
103108
return super().__bool__()
104109

@@ -109,7 +114,21 @@ def __getattr__(self, name):
109114
return LoggingUndefined
110115

111116

112-
def render_jinja2(template_filename: str, input_data: dict, logger: PgeLogger = None):
117+
def _try_parse_xml_string(s: str):
118+
_ = etree.fromstring(s.encode('utf-8'))
119+
120+
121+
JSON_VALIDATOR = json.loads
122+
YAML_VALIDATOR = partial(yaml.load, Loader=yaml.SafeLoader)
123+
XML_VALIDATOR = _try_parse_xml_string
124+
125+
126+
def render_jinja2(
127+
template_filename: str,
128+
input_data: dict,
129+
logger: PgeLogger = None,
130+
validator=None
131+
):
113132
"""
114133
Renders from a jinja2 template using the specified input data.
115134
Writes the rendered output to the specified output file.
@@ -126,6 +145,9 @@ def render_jinja2(template_filename: str, input_data: dict, logger: PgeLogger =
126145
will not raise exceptions or abort rendering. If not provided,
127146
the default Jinja2 error handling will apply for such errors,
128147
possibly including raised exceptions.
148+
validator:
149+
Callable which, if provided, takes the rendered text as an input and
150+
attempts to parse it. Must raise an exception if given invalid input.
129151
130152
Returns
131153
-------
@@ -155,10 +177,17 @@ def render_jinja2(template_filename: str, input_data: dict, logger: PgeLogger =
155177

156178
rendered_text = template.render(input_data)
157179

180+
if validator is not None:
181+
try:
182+
validator(rendered_text)
183+
except Exception as err:
184+
raise RuntimeError(f"Error rendering Jinja2 template: {err}") from err
185+
158186
return rendered_text
159187

160188

161189
def python_type_to_xml_type(obj) -> str:
190+
"""Returns a guess for the XML type of a Python object."""
162191
if isinstance(obj, str):
163192
if obj.lower() in ['true', 'false']:
164193
obj = obj.lower() == 'true'
@@ -172,6 +201,7 @@ def python_type_to_xml_type(obj) -> str:
172201

173202
return XML_TYPES[obj]
174203

204+
175205
class NumpyEncoder(json.JSONEncoder):
176206
"""Class to handle serialization of Numpy types during JSON enconding"""
177207
def default(self, obj):
@@ -186,5 +216,9 @@ def default(self, obj):
186216
return super(NumpyEncoder, self).default(obj)
187217

188218
def guess_attribute_display_name(var_name: str) -> str:
219+
"""
220+
Returns an approximation of an Additional Attribute's display name from its attribute name by converting snake_case
221+
to PascalCase.
222+
Ex. sample_attribute_name -> SampleAttributeName
223+
"""
189224
return var_name.title().replace('_', '')
190-

0 commit comments

Comments
 (0)