Skip to content

Commit

Permalink
remove shim and renderable, much cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
wpbonelli committed Nov 5, 2024
1 parent cbe304e commit 17c19f2
Show file tree
Hide file tree
Showing 15 changed files with 602 additions and 741 deletions.
3 changes: 2 additions & 1 deletion autotest/test_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from flopy.mf6.utils.codegen import make_all, make_targets
from flopy.mf6.utils.codegen.context import Context
from flopy.mf6.utils.codegen.dfn import Dfn
from flopy.mf6.utils.codegen.jinja import Filters

PROJ_ROOT = get_project_root_path()
MF6_PATH = PROJ_ROOT / "flopy" / "mf6"
Expand Down Expand Up @@ -38,7 +39,7 @@ def test_make_targets(dfn_name, function_tmpdir):

make_targets(dfn, function_tmpdir, verbose=True)
assert all(
(function_tmpdir / name.target).is_file()
(function_tmpdir / f"mf{Filters.Cls.title(name)}.py").is_file()
for name in Context.Name.from_dfn(dfn)
)

Expand Down
82 changes: 57 additions & 25 deletions flopy/mf6/utils/codegen/__init__.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,93 @@
from dataclasses import asdict
from itertools import chain
from os import PathLike
from pathlib import Path

from flopy.utils import import_optional_dependency

__all__ = ["make_targets", "make_all"]
__all__ = ["make_init", "make_targets", "make_all"]


def _get_template_env():
from flopy.mf6.utils.codegen.jinja import Filters

# import here instead of module so we don't
# expect optional deps at module init time
jinja = import_optional_dependency("jinja2")
loader = jinja.PackageLoader("flopy", "mf6/utils/codegen/templates/")
env = jinja.Environment(loader=loader)
env.filters["parent"] = Filters.parent
env.filters["prefix"] = Filters.prefix
env.filters["skip"] = Filters.skip

from flopy.mf6.utils.codegen.jinja import Filters

env.filters["base"] = Filters.Cls.base
env.filters["title"] = Filters.Cls.title
env.filters["description"] = Filters.Cls.description
env.filters["prefix"] = Filters.Cls.prefix
env.filters["parent"] = Filters.Cls.parent
env.filters["skip"] = Filters.Cls.skip

env.filters["attrs"] = Filters.Vars.attrs
env.filters["init"] = Filters.Vars.init

env.filters["type"] = Filters.Var.type

env.filters["nokw"] = Filters.nokw
env.filters["escape_trailing"] = Filters.escape_trailing
env.filters["value"] = Filters.value

return env


def make_init(dfns: dict, outdir: PathLike, verbose: bool = False):
"""Generate a Python __init__.py file for the given input definitions."""

from flopy.mf6.utils.codegen.context import Context

env = _get_template_env()
outdir = Path(outdir).expanduser()
contexts = [
c
for cc in [
[ctx for ctx in Context.from_dfn(dfn)] for dfn in dfns.values()
]
for c in cc
] # ugly, but it's the fastest way to flatten the list

from flopy.mf6.utils.codegen.context import Context

contexts = list(
chain(
*[[ctx for ctx in Context.from_dfn(dfn)] for dfn in dfns.values()]
)
)
target_name = "__init__.py"
target = outdir / target_name
target_path = outdir / target_name
template = env.get_template(f"{target_name}.jinja")
with open(target, "w") as f:
with open(target_path, "w") as f:
f.write(template.render(contexts=contexts))
if verbose:
print(f"Wrote {target}")
print(f"Wrote {target_path}")


def make_targets(dfn, outdir: PathLike, verbose: bool = False):
"""Generate Python source file(s) from the given input definition."""

from flopy.mf6.utils.codegen.context import Context

env = _get_template_env()
outdir = Path(outdir).expanduser()

from flopy.mf6.utils.codegen.context import Context
from flopy.mf6.utils.codegen.jinja import Filters

def _get_template_name(ctx_name) -> str:
"""The template file to use."""
base = Filters.Cls.base(ctx_name)
if base == "MFSimulationBase":
return "simulation.py.jinja"
elif base == "MFModel":
return "model.py.jinja"
elif base == "MFPackage":
if ctx_name.l == "exg":
return "exchange.py.jinja"
return "package.py.jinja"

for context in Context.from_dfn(dfn):
name = context.name
target = outdir / name.target
template = env.get_template(name.template)
with open(target, "w") as f:
f.write(template.render(**context.render()))
target_path = outdir / f"mf{Filters.Cls.title(name)}.py"
template_name = _get_template_name(name)
template = env.get_template(template_name)
with open(target_path, "w") as f:
f.write(template.render(**asdict(context)))
if verbose:
print(f"Wrote {target}")
print(f"Wrote {target_path}")


def make_all(dfndir: Path, outdir: Path, verbose: bool = False):
Expand Down
67 changes: 0 additions & 67 deletions flopy/mf6/utils/codegen/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@
)

from flopy.mf6.utils.codegen.dfn import Dfn, Ref, Vars
from flopy.mf6.utils.codegen.renderable import renderable
from flopy.mf6.utils.codegen.shim import SHIM


@renderable(**SHIM)
@dataclass
class Context:
"""
Expand Down Expand Up @@ -62,70 +59,6 @@ class Name(NamedTuple):
l: str
r: Optional[str]

@property
def title(self) -> str:
"""
The input context's unique title. This is not
identical to `f"{l}{r}` in some cases, but it
remains unique. The title is substituted into
the file name and class name.
"""
l, r = self
if self == ("sim", "nam"):
return "simulation"
if l is None:
return r
if r is None:
return l
if l == "sim":
return r
if l in ["sln", "exg"]:
return r
return l + r

@property
def base(self) -> str:
"""Base class from which the input context should inherit."""
_, r = self
if self == ("sim", "nam"):
return "MFSimulationBase"
if r is None:
return "MFModel"
return "MFPackage"

@property
def target(self) -> str:
"""The source file name to generate."""
return f"mf{self.title}.py"

@property
def template(self) -> str:
"""The template file to use."""
if self.base == "MFSimulationBase":
return "simulation.py.jinja"
elif self.base == "MFModel":
return "model.py.jinja"
elif self.base == "MFPackage":
if self.l == "exg":
return "exchange.py.jinja"
return "package.py.jinja"

@property
def description(self) -> str:
"""A description of the input context."""
l, r = self
title = self.title.title()
if self.base == "MFPackage":
return f"Modflow{title} defines a {r.upper()} package."
elif self.base == "MFModel":
return f"Modflow{title} defines a {l.upper()} model."
elif self.base == "MFSimulationBase":
return (
"MFSimulation is used to load, build, and/or save a MODFLOW 6 simulation."
" A MFSimulation object must be created before creating any of the MODFLOW"
" 6 model objects."
)

@staticmethod
def from_dfn(dfn: Dfn) -> List["Context.Name"]:
"""
Expand Down
Loading

0 comments on commit 17c19f2

Please sign in to comment.