Skip to content

Commit

Permalink
render-time transformations
Browse files Browse the repository at this point in the history
  • Loading branch information
wpbonelli committed Oct 4, 2024
1 parent 951e930 commit 16d5f09
Showing 1 changed file with 33 additions and 17 deletions.
50 changes: 33 additions & 17 deletions flopy/mf6/utils/createpackages.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
from pathlib import Path
from typing import (
Any,
Callable,
Dict,
ForwardRef,
Iterable,
Expand Down Expand Up @@ -155,26 +156,36 @@ def renderable(
*,
wrap_str: Optional[List[str]] = None,
keep_none: Optional[List[str]] = None,
transform: Optional[Dict[str, Callable[[Any], Any]]] = None,
):
"""
An object meant to be passed into a template
as a "rendered" dictionary, where "rendering"
means transforming key/value pairs to a form
more convenient for use within the template.
The object *must* be a dataclass.
appropriate for use within the template.
Notes
-----
Jinja supports attribute- and dictionary-
Transformations might be for convenience* or
to handle special cases where a variable has
edge cases or other need for alteration**.
*Jinja supports attribute- and dictionary-
based access but no arbitrary expressions,
and only a limited set of custom filters.
This can make it awkward to express some
things, so convert the dataclasses we'll
pass to `template.render(...)` to dicts,
with a few touchups.
things.
**This is convenient for handling complexity
incidental to the current mf6 data framework.
Transforming values at render time helps to
isolate special cases from the more general
templating infrastructure, so the framework
can be refactored more easily over time.
These include:
The object *must* be a dataclass.
Common use cases include:
- converting types to suitably qualified type names
- optionally removing key/value pairs whose value is None
- optionally quoting strings forming the RHS of an assignment or
Expand All @@ -190,22 +201,23 @@ def _render(d: dict) -> dict:
def _render_key(k):
return k

def _render_val(v):
return _try_get_type_name(_try_get_enum_value(v))
def _render_val(k, v):
v = _try_get_type_name(_try_get_enum_value(v))
t = transform.get(k, None) if transform else lambda v: v
return t(v)

# drop nones except where keep requested
_d = {
_render_key(k): _render_val(v)
_render_key(k): _render_val(k, v)
for k, v in d.items()
if (k in keep_none or v is not None)
}

# wrap string values where requested
if wrap_str:
for k in wrap_str:
v = _d.get(k, None)
if v is not None and isinstance(v, str):
_d[k] = f'"{v}"'
for k in wrap_str:
v = _d.get(k, None)
if v is not None and isinstance(v, str):
_d[k] = f'"{v}"'

return _d

Expand Down Expand Up @@ -641,7 +653,11 @@ def __init__(
Vars = Dict[str, Var]


@renderable(wrap_str=["default"], keep_none=["block", "default"])
@renderable(
wrap_str=["default"],
keep_none=["block", "default"],
# TODO replace the flags on Var with transforms?
)
@dataclass
class Context:
"""
Expand Down

0 comments on commit 16d5f09

Please sign in to comment.