|
5 | 5 | import hashlib |
6 | 6 | import importlib.resources as pkg_resources |
7 | 7 | from abc import ABC, abstractmethod |
8 | | -from collections.abc import Callable, Iterable |
| 8 | +from collections.abc import Callable |
9 | 9 | from functools import partial |
10 | 10 | from os import PathLike |
11 | 11 | from pathlib import Path |
@@ -96,73 +96,56 @@ class LocalRegistry(ModelRegistry): |
96 | 96 |
|
97 | 97 | exclude: ClassVar = [".DS_Store", "compare"] |
98 | 98 |
|
99 | | - def __init__( |
| 99 | + def __init__(self) -> None: |
| 100 | + self._paths: set[Path] = set() |
| 101 | + self._files: dict[str, Path] = {} |
| 102 | + self._models: dict[str, list[Path]] = {} |
| 103 | + self._examples: dict[str, list[str]] = {} |
| 104 | + |
| 105 | + def index( |
100 | 106 | self, |
101 | | - path: str | PathLike | Iterable[str | PathLike], |
102 | | - namefile_pattern: str = "mfsim.nam", |
| 107 | + path: str | PathLike, |
| 108 | + prefix: str = "", |
| 109 | + namefile: str = "mfsim.nam", |
103 | 110 | ): |
104 | 111 | """ |
105 | | - Create a registry from models under the given |
106 | | - directory path. |
| 112 | + Add models found under the given path to the registry. |
| 113 | +
|
| 114 | + Call this once or more to prepare a registry. If called on the same |
| 115 | + `path` again, the models will be reloaded — thus this method |
| 116 | + is idempotent and may be used to reload the registry e.g. if model |
| 117 | + files have changed since the registry was created. |
107 | 118 |
|
108 | 119 | The `path` may consist of model subdirectories |
109 | 120 | at arbitrary depth. Model input subdirectories |
110 | 121 | are identified by the presence of a namefile |
111 | 122 | matching `namefile_pattern`. |
112 | 123 | """ |
113 | | - # check if path is iterable of str\pathlike |
114 | | - if isinstance(path, Iterable) and not isinstance(path, str): |
115 | | - path = [Path(p).expanduser().resolve().absolute() for p in path] # type: ignore |
116 | | - missing = [p for p in path if not p.is_dir()] # type: ignore |
117 | | - if any(missing): |
118 | | - missing = [str(p) for p in missing] # type: ignore |
119 | | - raise NotADirectoryError( |
120 | | - f"Directory paths not found: {', '.join(missing)}" # type: ignore |
121 | | - ) |
122 | | - self._path = path |
123 | | - else: |
124 | | - path = Path(path).expanduser().resolve().absolute() |
125 | | - if not path.is_dir(): |
126 | | - raise NotADirectoryError(f"Directory path not found: {path}") |
127 | | - self._path = [path] |
128 | | - self._files: dict[str, dict[str, str | None]] = {} |
129 | | - self._models: dict[str, list[str]] = {} |
130 | | - self._examples: dict[str, list[str]] = {} |
131 | | - self.namefile_pattern = namefile_pattern |
132 | | - self.index() |
133 | 124 |
|
134 | | - def index(self): |
135 | | - """ |
136 | | - Build the registry from models found under the configured path. This |
137 | | - method can be called to reload the registry e.g. if model files have |
138 | | - changed since the registry was created. |
139 | | - """ |
| 125 | + path = Path(path).expanduser().resolve().absolute() |
| 126 | + if not path.is_dir(): |
| 127 | + raise NotADirectoryError(f"Directory path not found: {path}") |
| 128 | + self._paths.add(path) |
140 | 129 |
|
141 | | - self._files = {} |
142 | | - self._models = {} |
143 | | - self._examples = {} |
144 | | - for path in self._path: |
145 | | - model_paths = get_model_paths(path, namefile=self.namefile_pattern) |
146 | | - for model_path in model_paths: |
147 | | - model_path = model_path.expanduser().resolve().absolute() |
148 | | - rel_path = model_path.relative_to(path) |
149 | | - model_name = "/".join(rel_path.parts) |
150 | | - self._models[model_name] = [] |
151 | | - if len(rel_path.parts) > 1: |
152 | | - name = rel_path.parts[0] |
153 | | - if name not in self._examples: |
154 | | - self._examples[name] = [] |
155 | | - self._examples[name].append(model_name) |
156 | | - for p in model_path.rglob("*"): |
157 | | - if not p.is_file() or any( |
158 | | - e in p.name for e in LocalRegistry.exclude |
159 | | - ): |
160 | | - continue |
161 | | - relpath = p.expanduser().absolute().relative_to(path) |
162 | | - name = "/".join(relpath.parts) |
163 | | - hash = _sha256(p) |
164 | | - self._files[name] = {"hash": hash, "path": p, "relpath": relpath} |
165 | | - self._models[model_name].append(p) |
| 130 | + model_paths = get_model_paths(path, namefile=namefile) |
| 131 | + for model_path in model_paths: |
| 132 | + model_path = model_path.expanduser().resolve().absolute() |
| 133 | + rel_path = model_path.relative_to(path) |
| 134 | + parts = [prefix, *list(rel_path.parts)] if prefix else list(rel_path.parts) |
| 135 | + model_name = "/".join(parts) |
| 136 | + self._models[model_name] = [] |
| 137 | + if len(rel_path.parts) > 1: |
| 138 | + name = rel_path.parts[0] |
| 139 | + if name not in self._examples: |
| 140 | + self._examples[name] = [] |
| 141 | + self._examples[name].append(model_name) |
| 142 | + for p in model_path.rglob("*"): |
| 143 | + if not p.is_file() or any(e in p.name for e in LocalRegistry.exclude): |
| 144 | + continue |
| 145 | + relpath = p.expanduser().absolute().relative_to(path) |
| 146 | + name = "/".join(relpath.parts) |
| 147 | + self._files[name] = p |
| 148 | + self._models[model_name].append(p) |
166 | 149 |
|
167 | 150 | def copy_to( |
168 | 151 | self, workspace: str | PathLike, model_name: str, verbose: bool = False |
@@ -192,8 +175,8 @@ def copy_to( |
192 | 175 | return workspace |
193 | 176 |
|
194 | 177 | @property |
195 | | - def path(self) -> list[Path]: |
196 | | - return self._path # type: ignore |
| 178 | + def paths(self) -> set[Path]: |
| 179 | + return self._paths |
197 | 180 |
|
198 | 181 | @property |
199 | 182 | def files(self) -> dict: |
|
0 commit comments