From 3bd5f7edbd9be174f9289d3b50fe9c402ee6af63 Mon Sep 17 00:00:00 2001 From: Eric Callahan Date: Sat, 25 Dec 2021 07:55:19 -0500 Subject: [PATCH] template: add support for async rendering Signed-off-by: Eric Callahan --- moonraker/components/template.py | 48 ++++++++++++++++++++++---------- moonraker/confighelper.py | 8 ++++-- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/moonraker/components/template.py b/moonraker/components/template.py index a03c6ee..796abbf 100644 --- a/moonraker/components/template.py +++ b/moonraker/components/template.py @@ -25,8 +25,12 @@ class TemplateFactory: self.server = config.get_server() secrets: Secrets = self.server.load_component(config, 'secrets') self.jenv = jinja2.Environment('{%', '%}', '{', '}') + self.async_env = jinja2.Environment('{%', '%}', '{', '}', + enable_async=True) self.jenv.add_extension("jinja2.ext.do") self.jenv.filters['fromjson'] = json.loads + self.async_env.add_extension("jinja2.ext.do") + self.async_env.filters['fromjson'] = json.loads self.add_environment_global('raise_error', self._raise_error) self.add_environment_global('secrets', secrets) @@ -35,35 +39,49 @@ class TemplateFactory: raise self.server.error( f"Jinja 2 environment already contains global {name}") self.jenv.globals[name] = value + self.async_env.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) + def create_template(self, + source: str, + is_async: bool = False + ) -> JinjaTemplate: + env = self.async_env if is_async else self.jenv + try: + template = env.from_string(source) + except Exception: + logging.exception(f"Error creating template from source:\n{source}") + raise + return JinjaTemplate(source, self.server, template, is_async) + class JinjaTemplate: def __init__(self, source: str, server: Server, - env: jinja2.Environment + template: jinja2.Template, + is_async: bool ) -> 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 + self.orig_source = source + self.template = template + self.is_async = is_async def __str__(self) -> str: return self.orig_source + def render(self, context: Dict[str, Any] = {}) -> str: + if self.is_async: + raise self.server.error( + "Cannot render async templates with the render() method" + ", use render_async()") + return self.template.render(context).strip() + + async def render_async(self, context: Dict[str, Any] = {}) -> str: + ret = await self.template.render_async(context) + return ret.strip() + def load_component(config: ConfigHelper) -> TemplateFactory: return TemplateFactory(config) diff --git a/moonraker/confighelper.py b/moonraker/confighelper.py index a80e5f8..1d0d07f 100644 --- a/moonraker/confighelper.py +++ b/moonraker/confighelper.py @@ -333,6 +333,7 @@ class ConfigHelper: def gettemplate(self, option: str, default: Union[SentinelClass, _T] = SENTINEL, + is_async: bool = False, deprecate: bool = False ) -> Union[JinjaTemplate, _T]: try: @@ -345,7 +346,7 @@ class ConfigHelper: def gettemplate_wrapper(sec: str, opt: str) -> JinjaTemplate: val = self.config.get(sec, opt) - return template.create_template(val) + return template.create_template(val.strip(), is_async) return self._get_option(gettemplate_wrapper, option, default, deprecate=deprecate) @@ -353,13 +354,14 @@ class ConfigHelper: def load_template(self, option: str, default: Union[SentinelClass, str] = SENTINEL, + is_async: bool = False, deprecate: bool = False ) -> JinjaTemplate: - val = self.gettemplate(option, default, deprecate) + val = self.gettemplate(option, default, is_async, deprecate) if isinstance(val, str): template: TemplateFactory template = self.server.lookup_component('template') - return template.create_template(val) + return template.create_template(val.strip(), is_async) return val def read_supplemental_config(self, file_name: str) -> ConfigHelper: