Skip to content

Commit 04b9869

Browse files
committed
refactor(LocalRegistry): support multiple mode directory paths
1 parent d532950 commit 04b9869

1 file changed

Lines changed: 50 additions & 32 deletions

File tree

modflow_devtools/models.py

Lines changed: 50 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import hashlib
66
import importlib.resources as pkg_resources
77
from abc import ABC, abstractmethod
8-
from collections.abc import Callable
8+
from collections.abc import Callable, Iterable
99
from functools import partial
1010
from os import PathLike
1111
from pathlib import Path
@@ -65,7 +65,7 @@ def files(self) -> dict:
6565
@abstractmethod
6666
def models(self) -> dict:
6767
"""
68-
A map of model name to the model's input file set.
68+
A map of model name to the model's input files.
6969
"""
7070
...
7171

@@ -102,7 +102,11 @@ class LocalRegistry(ModelRegistry):
102102

103103
exclude: ClassVar = [".DS_Store", "compare"]
104104

105-
def __init__(self, path: str | PathLike, namefile_pattern: str = "mfsim.nam"):
105+
def __init__(
106+
self,
107+
path: str | PathLike | Iterable[str | PathLike],
108+
namefile_pattern: str = "mfsim.nam",
109+
):
106110
"""
107111
Create a registry from models under the given
108112
directory path.
@@ -112,12 +116,23 @@ def __init__(self, path: str | PathLike, namefile_pattern: str = "mfsim.nam"):
112116
are identified by the presence of a namefile
113117
matching `namefile_pattern`.
114118
"""
115-
self._path = Path(path).expanduser().resolve().absolute()
119+
# check if path is iterable of str\pathlike
120+
if isinstance(path, Iterable) and not isinstance(path, str):
121+
path = [Path(p).expanduser().resolve().absolute() for p in path]
122+
missing = [p for p in path if not p.is_dir()]
123+
if any(missing):
124+
raise NotADirectoryError(
125+
f"Directory paths not found: {', '.join(missing)}"
126+
)
127+
self._path = path
128+
else:
129+
path = Path(path).expanduser().resolve().absolute()
130+
if not path.is_dir():
131+
raise NotADirectoryError(f"Directory path not found: {path}")
132+
self._path = [path]
116133
self._files: dict[str, dict[str, str | None]] = {}
117134
self._models: dict[str, list[str]] = {}
118135
self._examples: dict[str, list[str]] = {}
119-
if not self._path.is_dir():
120-
raise NotADirectoryError(f"Path {self._path} is not a directory.")
121136
self.namefile_pattern = namefile_pattern
122137
self.index()
123138

@@ -131,25 +146,28 @@ def index(self):
131146
self._files = {}
132147
self._models = {}
133148
self._examples = {}
134-
model_paths = get_model_paths(self._path, namefile=self.namefile_pattern)
135-
for model_path in model_paths:
136-
model_path = model_path.expanduser().resolve().absolute()
137-
rel_path = model_path.relative_to(self._path)
138-
model_name = "/".join(rel_path.parts)
139-
self._models[model_name] = []
140-
if len(rel_path.parts) > 1:
141-
name = rel_path.parts[0]
142-
if name not in self._examples:
143-
self._examples[name] = []
144-
self._examples[name].append(model_name)
145-
for p in model_path.rglob("*"):
146-
if not p.is_file() or any(e in p.name for e in LocalRegistry.exclude):
147-
continue
148-
relpath = p.expanduser().absolute().relative_to(self._path)
149-
name = "/".join(relpath.parts)
150-
hash = _sha256(p)
151-
self._files[name] = {"hash": hash}
152-
self._models[model_name].append(name)
149+
for path in self._path:
150+
model_paths = get_model_paths(path, namefile=self.namefile_pattern)
151+
for model_path in model_paths:
152+
model_path = model_path.expanduser().resolve().absolute()
153+
rel_path = model_path.relative_to(path)
154+
model_name = "/".join(rel_path.parts)
155+
self._models[model_name] = []
156+
if len(rel_path.parts) > 1:
157+
name = rel_path.parts[0]
158+
if name not in self._examples:
159+
self._examples[name] = []
160+
self._examples[name].append(model_name)
161+
for p in model_path.rglob("*"):
162+
if not p.is_file() or any(
163+
e in p.name for e in LocalRegistry.exclude
164+
):
165+
continue
166+
relpath = p.expanduser().absolute().relative_to(path)
167+
name = "/".join(relpath.parts)
168+
hash = _sha256(p)
169+
self._files[name] = {"hash": hash, "path": p, "relpath": relpath}
170+
self._models[model_name].append(p)
153171

154172
def copy_to(
155173
self, workspace: str | PathLike, model_name: str, verbose: bool = False
@@ -159,7 +177,7 @@ def copy_to(
159177
The workspace will be created if it does not exist.
160178
"""
161179

162-
if not any(files := self.models.get(model_name, [])):
180+
if not any(file_paths := self.models.get(model_name, [])):
163181
return None
164182
# create the workspace if needed
165183
workspace = Path(workspace).expanduser().absolute()
@@ -169,13 +187,13 @@ def copy_to(
169187
# copy the files. some might be in nested folders,
170188
# but the first is guaranteed not to be, so use it
171189
# to determine relative path in the new workspace.
172-
base = Path(files[0]).parent
173-
for file in [Path(self._path) / f for f in files]:
190+
base = Path(file_paths[0]).parent
191+
for file_path in file_paths:
174192
if verbose:
175-
print(f"Copying {file} to workspace")
176-
path = workspace / file.relative_to(base)
177-
path.parent.mkdir(parents=True, exist_ok=True)
178-
copy(file, workspace / file.relative_to(base))
193+
print(f"Copying {file_path} to workspace")
194+
dest = workspace / file_path.relative_to(base)
195+
dest.parent.mkdir(parents=True, exist_ok=True)
196+
copy(file_path, dest)
179197
return workspace
180198

181199
@property

0 commit comments

Comments
 (0)