From eaafa49e4de9a4505034e84d17208f4b0fdeaa2a Mon Sep 17 00:00:00 2001 From: DavidNew-NOAA Date: Wed, 12 Jun 2024 19:13:04 +0000 Subject: [PATCH 1/8] Add template-replace filter for Jinja2 --- src/wxflow/jinja.py | 4 +++- src/wxflow/template.py | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/wxflow/jinja.py b/src/wxflow/jinja.py index 2ce7690..3f68e80 100644 --- a/src/wxflow/jinja.py +++ b/src/wxflow/jinja.py @@ -8,6 +8,7 @@ from .timetools import (add_to_datetime, strftime, to_fv3time, to_isotime, to_julian, to_timedelta, to_YMD, to_YMDH) +from .template import (replace_tmpl) __all__ = ['Jinja'] @@ -146,7 +147,8 @@ 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: replace_tmpl(string, tmpl_dict) + # Add any additional filters if filters is not None: for filter_name, filter_func in filters.items(): diff --git a/src/wxflow/template.py b/src/wxflow/template.py index ad623c2..10c24ff 100644 --- a/src/wxflow/template.py +++ b/src/wxflow/template.py @@ -189,3 +189,12 @@ def is_single_type_or_string(s): return True else: return False + +def replace_tmpl(string, tmpl_dict): + """ + Replace substrings of input string using input dictionary. + """ + + for key in tmpl_dict: + string = string.replace(key, tmpl_dict[key]) + return string From 6b166baaf222a5add850609975f61b3f7e4ecdd7 Mon Sep 17 00:00:00 2001 From: DavidNew-NOAA Date: Thu, 13 Jun 2024 15:58:05 +0000 Subject: [PATCH 2/8] Change copy() to deepcopy() --- src/wxflow/task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 91355ba392644ea68c2e44f534202a2a6e6593c2 Mon Sep 17 00:00:00 2001 From: DavidNew-NOAA Date: Thu, 13 Jun 2024 16:27:48 +0000 Subject: [PATCH 3/8] pynorms --- src/wxflow/jinja.py | 4 ++-- src/wxflow/template.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wxflow/jinja.py b/src/wxflow/jinja.py index 3f68e80..b8399e3 100644 --- a/src/wxflow/jinja.py +++ b/src/wxflow/jinja.py @@ -6,9 +6,9 @@ import jinja2 from markupsafe import Markup +from .template import (replace_tmpl) from .timetools import (add_to_datetime, strftime, to_fv3time, to_isotime, to_julian, to_timedelta, to_YMD, to_YMDH) -from .template import (replace_tmpl) __all__ = ['Jinja'] @@ -148,7 +148,7 @@ def get_set_env(self, loader: jinja2.BaseLoader, filters: Dict[str, callable] = 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: replace_tmpl(string, tmpl_dict) - + # Add any additional filters if filters is not None: for filter_name, filter_func in filters.items(): diff --git a/src/wxflow/template.py b/src/wxflow/template.py index 10c24ff..c8e3700 100644 --- a/src/wxflow/template.py +++ b/src/wxflow/template.py @@ -190,6 +190,7 @@ def is_single_type_or_string(s): else: return False + def replace_tmpl(string, tmpl_dict): """ Replace substrings of input string using input dictionary. From c589019f4f3c63886b5fd8048905bfe293899b51 Mon Sep 17 00:00:00 2001 From: DavidNew-NOAA Date: Thu, 13 Jun 2024 16:29:48 +0000 Subject: [PATCH 4/8] pynorms --- src/wxflow/jinja.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wxflow/jinja.py b/src/wxflow/jinja.py index b8399e3..6c20f8d 100644 --- a/src/wxflow/jinja.py +++ b/src/wxflow/jinja.py @@ -6,7 +6,7 @@ import jinja2 from markupsafe import Markup -from .template import (replace_tmpl) +from .template import replace_tmpl from .timetools import (add_to_datetime, strftime, to_fv3time, to_isotime, to_julian, to_timedelta, to_YMD, to_YMDH) From ae7be754239b27d8cf8a64d4b52acd4788c22c23 Mon Sep 17 00:00:00 2001 From: DavidNew-NOAA Date: Fri, 14 Jun 2024 17:02:38 +0000 Subject: [PATCH 5/8] Add test --- src/wxflow/jinja.py | 4 ++-- src/wxflow/template.py | 10 ---------- tests/test_jinja.py | 5 +++++ 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/wxflow/jinja.py b/src/wxflow/jinja.py index 6c20f8d..642bb83 100644 --- a/src/wxflow/jinja.py +++ b/src/wxflow/jinja.py @@ -5,8 +5,8 @@ import jinja2 from markupsafe import Markup +from functools import reduce -from .template import replace_tmpl from .timetools import (add_to_datetime, strftime, to_fv3time, to_isotime, to_julian, to_timedelta, to_YMD, to_YMDH) @@ -147,7 +147,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: replace_tmpl(string, tmpl_dict) + 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/template.py b/src/wxflow/template.py index c8e3700..ad623c2 100644 --- a/src/wxflow/template.py +++ b/src/wxflow/template.py @@ -189,13 +189,3 @@ def is_single_type_or_string(s): return True else: return False - - -def replace_tmpl(string, tmpl_dict): - """ - Replace substrings of input string using input dictionary. - """ - - for key in tmpl_dict: - string = string.replace(key, tmpl_dict[key]) - return string diff --git a/tests/test_jinja.py b/tests/test_jinja.py index 2ba3751..57ff801 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,10 @@ 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)} + env = Jinja.get_set_env(jinja2.BaseLoader()) + 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): From a26ca2d72da2bd236ce3c8e0b9cc954a60017bf2 Mon Sep 17 00:00:00 2001 From: DavidNew-NOAA Date: Fri, 14 Jun 2024 17:09:12 +0000 Subject: [PATCH 6/8] Pynorms --- src/wxflow/jinja.py | 2 +- tests/test_jinja.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/wxflow/jinja.py b/src/wxflow/jinja.py index 642bb83..c37d6fc 100644 --- a/src/wxflow/jinja.py +++ b/src/wxflow/jinja.py @@ -1,11 +1,11 @@ import os import sys +from functools import reduce from pathlib import Path from typing import Dict, List, Union import jinja2 from markupsafe import Markup -from functools import reduce from .timetools import (add_to_datetime, strftime, to_fv3time, to_isotime, to_julian, to_timedelta, to_YMD, to_YMDH) diff --git a/tests/test_jinja.py b/tests/test_jinja.py index 57ff801..7c00587 100644 --- a/tests/test_jinja.py +++ b/tests/test_jinja.py @@ -31,9 +31,11 @@ def test_render_stream(): 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)} - env = Jinja.get_set_env(jinja2.BaseLoader()) + 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): From 98b56617d6e9eaf4107087e0c176b89a7ee95358 Mon Sep 17 00:00:00 2001 From: DavidNew-NOAA Date: Fri, 14 Jun 2024 17:13:34 +0000 Subject: [PATCH 7/8] Add another test --- tests/test_jinja.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_jinja.py b/tests/test_jinja.py index 7c00587..e9ffde8 100644 --- a/tests/test_jinja.py +++ b/tests/test_jinja.py @@ -48,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): From 6a790fcf5d5c20e1b797bcceea04798f192f9fb3 Mon Sep 17 00:00:00 2001 From: DavidNew-NOAA Date: Fri, 14 Jun 2024 17:18:08 +0000 Subject: [PATCH 8/8] Add documentation to replace_tmpl filter --- src/wxflow/jinja.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wxflow/jinja.py b/src/wxflow/jinja.py index c37d6fc..a5ffa85 100644 --- a/src/wxflow/jinja.py +++ b/src/wxflow/jinja.py @@ -111,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.