diff --git a/flopy4/cli.py b/flopy4/cli.py index ededeb9c..e9689c18 100644 --- a/flopy4/cli.py +++ b/flopy4/cli.py @@ -140,8 +140,9 @@ def _write_contract(outdir: Path, mf6_version: str) -> None: is_branch = not effective_version[0].isdigit() if effective_version else True _populate_remote_cache(registry, release_id, force=args.force or is_branch) + dfns_spec = registry.spec(schema_version=_DFN_SCHEMA_VERSION) make( - dfndir=registry.cache_path if isinstance(registry, RemoteDfnRegistry) else registry.path, + dfns=dfns_spec, outdir=_MF6_ROOT, existing_only=not args.all_packages, makedirs=args.all_packages, diff --git a/flopy4/mf6/utils/codegen/dfn2py.py b/flopy4/mf6/utils/codegen/dfn2py.py index 3536f5b1..11c44d54 100644 --- a/flopy4/mf6/utils/codegen/dfn2py.py +++ b/flopy4/mf6/utils/codegen/dfn2py.py @@ -5,8 +5,6 @@ from os import PathLike from pathlib import Path -import modflow_devtools.dfn as dfn - from flopy4.mf6.utils.codegen.make import make_modules _PROJ_ROOT = Path(__file__).parents[4].expanduser().resolve() @@ -41,18 +39,22 @@ def make( - dfndir: str | PathLike, + dfns: dict, outdir: str | PathLike = _MF6_ROOT, developmode: bool = False, makedirs: bool = False, existing_only: bool = False, verbose: bool = False, ): - """Generate an MF6 module from DFNs.""" - dfndir = Path(dfndir).expanduser().resolve() + """Generate an MF6 module from DFNs. + + Parameters + ---------- + dfns : + Pre-loaded DFN dict, e.g. from a registry's `spec()` call. + """ outdir = Path(outdir).expanduser().resolve() outdir.mkdir(exist_ok=True, parents=True) - dfns = dfn.Dfn.load_all(dfndir, schema_version="2.0.0.dev1") components = make_modules( dfns=dfns, outdir=outdir, @@ -99,9 +101,14 @@ def cli_main() -> None: parser.add_argument("--verbose", action="store_true") args = parser.parse_args() + from modflow_devtools.dfns import LocalDfnRegistry + + registry = LocalDfnRegistry(path=Path(args.dfndir)) + dfns = registry.spec(schema_version="2.0.0.dev1") + try: make( - dfndir=args.dfndir, + dfns=dfns, outdir=args.outdir, developmode=args.developmode, makedirs=args.makedirs, diff --git a/flopy4/mf6/utils/codegen/make.py b/flopy4/mf6/utils/codegen/make.py index f5638856..ada47314 100644 --- a/flopy4/mf6/utils/codegen/make.py +++ b/flopy4/mf6/utils/codegen/make.py @@ -1101,8 +1101,7 @@ def make_module(spec: ComponentSpec, env: jinja2.Environment, verbose: bool = Fa def make_modules( *, - dfns: dict[str, "Dfn"] | None = None, - dfndir: PathLike | None = None, + dfns: dict[str, "Dfn"], outdir: PathLike, developmode: bool = False, skip: set[str] | None = None, @@ -1115,10 +1114,7 @@ def make_modules( Parameters ---------- dfns : - Pre-loaded DFN dict from a registry's spec() call. Takes precedence - over dfndir when both are provided. - dfndir : - Directory containing DFN files. Used only when dfns is not provided. + Pre-loaded DFN dict, e.g. from a registry's spec() call. outdir : Root output directory for generated Python files. developmode : @@ -1137,10 +1133,6 @@ def make_modules( list[ComponentSpec] Specs for all components that were generated. """ - if dfns is None: - if dfndir is None: - raise ValueError("Provide either 'dfns' or 'dfndir'.") - dfns = Dfn.load_all(Path(dfndir), schema_version="2.0.0.dev1") outdir = Path(outdir) skip = skip or set() env = _get_env() diff --git a/test/mf6/test_mf6_codegen.py b/test/mf6/test_mf6_codegen.py index 6474bb9d..76f0eb0d 100644 --- a/test/mf6/test_mf6_codegen.py +++ b/test/mf6/test_mf6_codegen.py @@ -684,12 +684,12 @@ def test_npf_compound_records_expanded(all_dfns): # Layer 3: End-to-end generation -def test_simple_tier_generates_importable_files(dfn_path, tmp_path, all_dfns): +def test_simple_tier_generates_importable_files(tmp_path, all_dfns): """Run make_all() and verify each simple-tier file is importable with the right class.""" (tmp_path / "gwf").mkdir() skip = {n for n in all_dfns if n not in SIMPLE_TIER} - specs = make_modules(dfndir=dfn_path, outdir=tmp_path, skip=skip) + specs = make_modules(dfns=all_dfns, outdir=tmp_path, skip=skip) generated = {s.dfn_name: s for s in specs} @@ -722,12 +722,12 @@ def test_simple_tier_generates_importable_files(dfn_path, tmp_path, all_dfns): assert hasattr(mod, expected_class), f"Class {expected_class} not found in {spec.outpath}" -def test_solution_tier_generates_importable_files(dfn_path, tmp_path, all_dfns): +def test_solution_tier_generates_importable_files(tmp_path, all_dfns): """Run make_all() and verify each solution-tier file is importable with Solution base.""" from flopy4.mf6.solution import Solution skip = {n for n in all_dfns if n not in SOLUTION_TIER} - specs = make_modules(dfndir=dfn_path, outdir=tmp_path, skip=skip) + specs = make_modules(dfns=all_dfns, outdir=tmp_path, skip=skip) generated = {s.dfn_name: s for s in specs} @@ -780,14 +780,14 @@ def _load_class_from_spec(spec, mod_name: str, expected_class: str): del sys.modules[key] -def test_transport_tier_generates_importable_files(dfn_path, tmp_path, all_dfns): +def test_transport_tier_generates_importable_files(tmp_path, all_dfns): """gwt-disv, gwt-ist, gwe-disv generate importable Package subclasses.""" for subdir in ("gwt", "gwe"): (tmp_path / subdir).mkdir() target = {n for n in TRANSPORT_TIER if n in all_dfns} skip = {n for n in all_dfns if n not in target} - specs = make_modules(dfndir=dfn_path, outdir=tmp_path, skip=skip) + specs = make_modules(dfns=all_dfns, outdir=tmp_path, skip=skip) generated = {s.dfn_name: s for s in specs} from flopy4.mf6.package import Package @@ -802,14 +802,14 @@ def test_transport_tier_generates_importable_files(dfn_path, tmp_path, all_dfns) assert issubclass(cls, Package) -def test_oc_tier_generates_importable_files(dfn_path, tmp_path, all_dfns): +def test_oc_tier_generates_importable_files(tmp_path, all_dfns): """gwt-oc, gwe-oc, prt-oc generate importable Package subclasses with OC period fields.""" for subdir in ("gwt", "gwe", "prt"): (tmp_path / subdir).mkdir() target = {n for n in OC_TIER if n in all_dfns} skip = {n for n in all_dfns if n not in target} - specs = make_modules(dfndir=dfn_path, outdir=tmp_path, skip=skip) + specs = make_modules(dfns=all_dfns, outdir=tmp_path, skip=skip) generated = {s.dfn_name: s for s in specs} from flopy4.mf6.package import Package @@ -827,13 +827,13 @@ def test_oc_tier_generates_importable_files(dfn_path, tmp_path, all_dfns): assert oc_fields, f"{dfn_name} should have save_/print_ period fields" -def test_utl_tier_generates_importable_files(dfn_path, tmp_path, all_dfns): +def test_utl_tier_generates_importable_files(tmp_path, all_dfns): """utl-* packages generate importable Package subclasses (including utl-tas inner classes).""" (tmp_path / "utl").mkdir() target = {n for n in UTL_TIER if n in all_dfns} skip = {n for n in all_dfns if n not in target} - specs = make_modules(dfndir=dfn_path, outdir=tmp_path, makedirs=True, skip=skip) + specs = make_modules(dfns=all_dfns, outdir=tmp_path, makedirs=True, skip=skip) generated = {s.dfn_name: s for s in specs} from flopy4.mf6.package import Package @@ -848,13 +848,13 @@ def test_utl_tier_generates_importable_files(dfn_path, tmp_path, all_dfns): assert issubclass(cls, Package) -def test_exg_tier_generates_importable_files(dfn_path, tmp_path, all_dfns): +def test_exg_tier_generates_importable_files(tmp_path, all_dfns): """exg-* packages generate importable Package subclasses (including 0-field pass-only).""" (tmp_path / "exg").mkdir() target = {n for n in EXG_TIER if n in all_dfns} skip = {n for n in all_dfns if n not in target} - specs = make_modules(dfndir=dfn_path, outdir=tmp_path, makedirs=True, skip=skip) + specs = make_modules(dfns=all_dfns, outdir=tmp_path, makedirs=True, skip=skip) generated = {s.dfn_name: s for s in specs} from flopy4.mf6.package import Package