From f6938c784aaf82bff735da9cf6b712701a428374 Mon Sep 17 00:00:00 2001 From: DavidNew-NOAA <134300700+DavidNew-NOAA@users.noreply.github.com> Date: Fri, 14 Jun 2024 13:28:26 -0400 Subject: [PATCH] Create replace_tmpl filter for Jinja2 (#33) --- src/wxflow/jinja.py | 3 +++ src/wxflow/task.py | 2 +- tests/test_jinja.py | 13 +++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/wxflow/jinja.py b/src/wxflow/jinja.py index 2ce7690..a5ffa85 100644 --- a/src/wxflow/jinja.py +++ b/src/wxflow/jinja.py @@ -1,5 +1,6 @@ import os import sys +from functools import reduce from pathlib import Path from typing import Dict, List, Union @@ -110,6 +111,7 @@ def get_set_env(self, loader: jinja2.BaseLoader, filters: Dict[str, callable] = getenv: read variable from environment if defined, else UNDEFINED to_timedelta: convert a string to a timedelta object add_to_datetime: add time to a datetime, return new datetime object + replace_tmpl: replace substrings of an input string with replacements specified by an input dictionary The Expression Statement extension "jinja2.ext.do", which enables {% do ... %} statements. These are useful for appending to lists. @@ -146,6 +148,7 @@ def get_set_env(self, loader: jinja2.BaseLoader, filters: Dict[str, callable] = if not (isinstance(dt, SilentUndefined) or isinstance(delta, SilentUndefined)) else dt if isinstance(dt, SilentUndefined) else delta) env.filters["to_timedelta"] = lambda delta_str: to_timedelta(delta_str) if not isinstance(delta_str, SilentUndefined) else delta_str + env.filters["replace_tmpl"] = lambda string, tmpl_dict: reduce(lambda ss, kk: ss.replace(kk, tmpl_dict[kk]), tmpl_dict, string) # Add any additional filters if filters is not None: diff --git a/src/wxflow/task.py b/src/wxflow/task.py index 80ef4a2..cbfb590 100644 --- a/src/wxflow/task.py +++ b/src/wxflow/task.py @@ -41,7 +41,7 @@ def __init__(self, config: Dict, *args, **kwargs): # Create task_config with everything that is inside _config and whatever the user chooses to # extend it with when initializing a child subclass of Task. Only task_config should be referenced # in any application, not _config. - self.task_config = self._config.copy() + self.task_config = self._config.deepcopy() # Any other composite runtime variables that may be needed for the duration of the task # can be constructed here diff --git a/tests/test_jinja.py b/tests/test_jinja.py index 2ba3751..e9ffde8 100644 --- a/tests/test_jinja.py +++ b/tests/test_jinja.py @@ -1,5 +1,6 @@ from datetime import datetime +import jinja2 import pytest from wxflow import Jinja, to_isotime @@ -29,6 +30,12 @@ def test_render_stream(): j = Jinja(j2tmpl, data, allow_missing=False) assert j.render == f"Hello Jane! How are you? It is: {to_isotime(current_date)}" + tmpl_dict = {"{{ name }}": "Jane", "{{ greeting }}": "How are you?", "{{ current_date | to_isotime }}": to_isotime(current_date)} + j = Jinja(j2tmpl, data, allow_missing=False) + loader = jinja2.BaseLoader() + env = j.get_set_env(loader) + assert env.filters['replace_tmpl'](j2tmpl, tmpl_dict) == f"Hello Jane! How are you? It is: {to_isotime(current_date)}" + def test_render_file(tmp_path, create_template): @@ -41,6 +48,12 @@ def test_render_file(tmp_path, create_template): j = Jinja(str(file_path), data, allow_missing=False) assert j.render == f"Hello Jane! How are you? It is: {to_isotime(current_date)}" + tmpl_dict = {"{{ name }}": "Jane", "{{ greeting }}": "How are you?", "{{ current_date | to_isotime }}": to_isotime(current_date)} + j = Jinja(str(file_path), data, allow_missing=False) + loader = jinja2.BaseLoader() + env = j.get_set_env(loader) + assert env.filters['replace_tmpl'](j2tmpl, tmpl_dict) == f"Hello Jane! How are you? It is: {to_isotime(current_date)}" + def test_include(tmp_path, create_template):