|
| 1 | +"""A decoder for jinja2 3.0.x formatted strings. |
| 2 | +""" |
| 3 | +from typing import Dict, Optional, Set, Tuple |
| 4 | + |
| 5 | +import jinja2 |
| 6 | +from jinja2.exceptions import TemplateSyntaxError |
| 7 | +from jinja2.meta import find_undeclared_variables |
| 8 | + |
| 9 | + |
| 10 | +def decode(template_text: str, |
| 11 | + variable_map: Optional[Dict[str, str]], |
| 12 | + subject: str) -> Tuple[str, bool]: |
| 13 | + """Decodes text expected to conform to Jinja2 (v3.0.x) |
| 14 | + """ |
| 15 | + assert template_text |
| 16 | + assert subject |
| 17 | + |
| 18 | + # Make a template from the text |
| 19 | + env: jinja2.Environment = jinja2.\ |
| 20 | + Environment(undefined=jinja2.DebugUndefined) |
| 21 | + try: |
| 22 | + template: jinja2.Template = env.from_string(template_text) |
| 23 | + except TemplateSyntaxError as ex: |
| 24 | + msg: str = f'TemplateSyntaxError with {subject}: {ex}' |
| 25 | + return msg, False |
| 26 | + |
| 27 | + # Render (this works even if there are variables in the rendered text |
| 28 | + # The rendered text, when stripped of whitespace, must not be empty. |
| 29 | + rendered_text = template.render(variable_map).strip() |
| 30 | + if len(rendered_text) == 0: |
| 31 | + msg = f'Rendered text for {subject} is blank' |
| 32 | + return msg, False |
| 33 | + |
| 34 | + # Check if rendering was done correctly. |
| 35 | + # It's a little odd with Jinja2 - as undefined variables are not |
| 36 | + # considered errors. See https://stackoverflow.com/a/55699590 in the |
| 37 | + # StackOverflow topic: - |
| 38 | + # How to get ALL undefined variables from a Jinja2 template? |
| 39 | + abstract_syntax_tree = env.parse(rendered_text) |
| 40 | + undefined: Set[str] = find_undeclared_variables(abstract_syntax_tree) |
| 41 | + if undefined: |
| 42 | + # We |
| 43 | + msg = f'Undefined template variables for {subject}:' |
| 44 | + for variable in undefined: |
| 45 | + msg += f' {variable},' |
| 46 | + # Return the message, stripping the last comma from it |
| 47 | + return msg[:-1], False |
| 48 | + |
| 49 | + # OK if we get here. |
| 50 | + # Just return the rendered text |
| 51 | + return rendered_text, True |
0 commit comments