From 2142d344dd315971bee39269383b5843ff0e430c Mon Sep 17 00:00:00 2001 From: Eric Callahan Date: Thu, 18 Nov 2021 06:13:59 -0500 Subject: [PATCH] template: add support for Jinja2 template evaluation Signed-off-by: Eric Callahan --- moonraker/components/template.py | 66 ++++++++++++++++++++++++++++++ scripts/moonraker-requirements.txt | 1 + 2 files changed, 67 insertions(+) create mode 100644 moonraker/components/template.py diff --git a/moonraker/components/template.py b/moonraker/components/template.py new file mode 100644 index 0000000..9720090 --- /dev/null +++ b/moonraker/components/template.py @@ -0,0 +1,66 @@ +# Template Factory helper +# +# Copyright (C) 2021 Eric Callahan +# +# This file may be distributed under the terms of the GNU GPLv3 license. +from __future__ import annotations +import logging +import jinja2 +import json + +# Annotation imports +from typing import ( + TYPE_CHECKING, + Any, + Dict +) + +if TYPE_CHECKING: + from moonraker import Server + from confighelper import ConfigHelper + +class TemplateFactory: + def __init__(self, config: ConfigHelper) -> None: + self.server = config.get_server() + self.jenv = jinja2.Environment('{%', '%}', '{', '}') + self.jenv.add_extension("jinja2.ext.do") + self.jenv.filters['fromjson'] = json.loads + self.add_environment_global('raise_error', self._raise_error) + + def add_environment_global(self, name: str, value: Any): + if name in self.jenv.globals: + raise self.server.error( + f"Jinja 2 environment already contains global {name}") + self.jenv.globals[name] = value + + def _raise_error(self, err_msg: str, err_code: int = 400) -> None: + raise self.server.error(err_msg, err_code) + + def create_template(self, source: str) -> JinjaTemplate: + return JinjaTemplate(source, self.server, self.jenv) + +class JinjaTemplate: + def __init__(self, + source: str, + server: Server, + env: jinja2.Environment + ) -> None: + self.server = server + self.orig_source = source.strip() + try: + self.template = env.from_string(self.orig_source) + except Exception: + logging.exception(f"Error creating template from source:\n{source}") + raise + + def render(self, context: Dict[str, Any] = {}) -> str: + try: + return self.template.render(context).strip() + except Exception as e: + raise self.server.error("Error rendering template") from e + + def __str__(self) -> str: + return self.orig_source + +def load_component(config: ConfigHelper) -> TemplateFactory: + return TemplateFactory(config) diff --git a/scripts/moonraker-requirements.txt b/scripts/moonraker-requirements.txt index c742da9..750780d 100644 --- a/scripts/moonraker-requirements.txt +++ b/scripts/moonraker-requirements.txt @@ -11,3 +11,4 @@ paho-mqtt==1.5.1 pycurl==7.44.1 zeroconf==0.37.0 preprocess-cancellation==0.1.6 +jinja2==3.0.3