Skip to content

Commit 427b2f1

Browse files
committed
revert models/programs api changes
1 parent a8c7c9b commit 427b2f1

4 files changed

Lines changed: 96 additions & 41 deletions

File tree

modflow_devtools/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ def _sync_all():
4747
# Sync Models
4848
print("=== Models ===")
4949
try:
50-
from modflow_devtools.models import ModelSources
50+
from modflow_devtools.models import ModelSourceConfig
5151

52-
config = ModelSources.load()
52+
config = ModelSourceConfig.load()
5353
config.sync()
5454
print("Models synced successfully")
5555
except Exception as e:

modflow_devtools/models/__init__.py

Lines changed: 88 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -442,8 +442,8 @@ class DiscoveredModelRegistry:
442442
url: str
443443

444444

445-
class ModelSource(BaseModel):
446-
"""Model source repository configuration."""
445+
class ModelSourceRepo(BaseModel):
446+
"""A single source model repository in the bootstrap file."""
447447

448448
@dataclass
449449
class SyncResult:
@@ -462,14 +462,18 @@ class SyncStatus:
462462
cached_refs: list[str]
463463
missing_refs: list[str]
464464

465+
repo: str = Field(..., description="Repository in format 'owner/name'")
465466
name: str = Field(
466467
..., description="Name for model addressing (injected from key if not explicit)"
467468
)
468-
repo: str = Field(..., description="Repository in format 'owner/name'")
469469
refs: list[str] = Field(
470470
default_factory=list,
471471
description="Default refs to sync (branches, tags, or commit hashes)",
472472
)
473+
registry_path: str = Field(
474+
default=".registry",
475+
description="Path to registry directory in repository",
476+
)
473477

474478
@field_validator("repo")
475479
@classmethod
@@ -595,9 +599,9 @@ def sync(
595599
if not refs:
596600
if verbose:
597601
print(f"No refs configured for source '{source_name}', aborting")
598-
return ModelSource.SyncResult()
602+
return ModelSourceRepo.SyncResult()
599603

600-
result = ModelSource.SyncResult()
604+
result = ModelSourceRepo.SyncResult()
601605

602606
for ref in refs:
603607
if not force and _DEFAULT_CACHE.has(source_name, ref):
@@ -658,41 +662,92 @@ def list_synced_refs(self) -> list[str]:
658662
return [ref for source, ref in cached if source == self.name]
659663

660664

661-
class ModelSources(BaseModel):
662-
"""Configuration for multiple model source repositories."""
665+
class ModelSourceConfig(BaseModel):
666+
"""Model source configuration file structure."""
663667

664-
sources: dict[str, ModelSource] = Field(
665-
default_factory=dict, description="Model source repositories"
668+
sources: dict[str, ModelSourceRepo] = Field(
669+
..., description="Map of source names to source metadata"
666670
)
667671

668672
@classmethod
669673
def load(
670674
cls,
671-
path: str | PathLike | None = None,
672-
) -> "ModelSources":
673-
"""Load model source configurations from a TOML file."""
674-
path = Path(path)
675-
if not path.exists():
676-
return cls()
677-
678-
with path.open("rb") as f:
679-
data = tomli.load(f)
675+
bootstrap_path: str | PathLike | None = None,
676+
user_config_path: str | PathLike | None = None,
677+
) -> "ModelSourceConfig":
678+
"""
679+
Load model source configuration.
680680
681-
sources = {}
682-
for name, config in data.get("sources", {}).items():
683-
sources[name] = ModelSource(**config)
681+
Parameters
682+
----------
683+
bootstrap_path : str | PathLike | None
684+
Path to bootstrap config file. If None, uses bundled default.
685+
If provided, ONLY this file is loaded (no user config overlay unless specified).
686+
user_config_path : str | PathLike | None
687+
Path to user config file to overlay on top of bootstrap.
688+
If None and bootstrap_path is None, attempts to load from default user config location.
684689
685-
return cls(sources=sources)
690+
Returns
691+
-------
692+
ModelSourceConfig
693+
Loaded and merged configuration
694+
"""
695+
# Load base config
696+
if bootstrap_path is not None:
697+
# Explicit bootstrap path - only load this file
698+
with Path(bootstrap_path).open("rb") as f:
699+
cfg = tomli.load(f)
700+
else:
701+
# Use bundled default
702+
with _DEFAULT_CONFIG_PATH.open("rb") as f:
703+
cfg = tomli.load(f)
704+
705+
# If no explicit bootstrap path, try to load user config overlay
706+
if user_config_path is None:
707+
user_config_path = get_user_config_path()
708+
709+
# Overlay user config if specified or found
710+
if user_config_path is not None:
711+
user_path = Path(user_config_path)
712+
if user_path.exists():
713+
with user_path.open("rb") as f:
714+
user_cfg = tomli.load(f)
715+
# Merge user config sources into base config
716+
if "sources" in user_cfg:
717+
if "sources" not in cfg:
718+
cfg["sources"] = {}
719+
cfg["sources"] = cfg["sources"] | user_cfg["sources"]
720+
721+
# inject source names if not explicitly provided
722+
for name, src in cfg.get("sources", {}).items():
723+
if "name" not in src:
724+
src["name"] = name
725+
726+
return cls(**cfg)
686727

687728
@classmethod
688-
def merge(cls, base: "ModelSources", overlay: "ModelSources") -> "ModelSources":
689-
"""Merge two source configurations. Overlay takes precedence."""
690-
merged = dict(base.sources)
691-
merged.update(overlay.sources)
692-
return cls(sources=merged)
729+
def merge(cls, base: "ModelSourceConfig", overlay: "ModelSourceConfig") -> "ModelSourceConfig":
730+
"""
731+
Merge two configurations, with overlay taking precedence.
732+
733+
Parameters
734+
----------
735+
base : ModelSourceConfig
736+
Base configuration
737+
overlay : ModelSourceConfig
738+
Configuration to overlay on top of base
739+
740+
Returns
741+
-------
742+
ModelSourceConfig
743+
Merged configuration
744+
"""
745+
merged_sources = base.sources.copy()
746+
merged_sources.update(overlay.sources)
747+
return cls(sources=merged_sources)
693748

694749
@property
695-
def status(self) -> dict[str, ModelSource.SyncStatus]:
750+
def status(self) -> dict[str, ModelSourceRepo.SyncStatus]:
696751
"""
697752
Sync status for all configured model source repositories.
698753
@@ -717,7 +772,7 @@ def status(self) -> dict[str, ModelSource.SyncStatus]:
717772
else:
718773
missing.append(ref)
719774

720-
status[name] = ModelSource.SyncStatus(
775+
status[name] = ModelSourceRepo.SyncStatus(
721776
repo=source.repo,
722777
configured_refs=refs,
723778
cached_refs=cached,
@@ -728,10 +783,10 @@ def status(self) -> dict[str, ModelSource.SyncStatus]:
728783

729784
def sync(
730785
self,
731-
source: str | ModelSource | None = None,
786+
source: str | ModelSourceRepo | None = None,
732787
force: bool = False,
733788
verbose: bool = False,
734-
) -> dict[str, ModelSource.SyncResult]:
789+
) -> dict[str, ModelSourceRepo.SyncResult]:
735790
"""
736791
Synchronize registry files from model source(s).
737792
@@ -753,7 +808,7 @@ def sync(
753808
"""
754809

755810
if source:
756-
if isinstance(source, ModelSource):
811+
if isinstance(source, ModelSourceRepo):
757812
if source.name not in self.sources:
758813
raise ValueError(f"Source '{source.name}' not found in bootstrap")
759814
sources = [source]
@@ -1249,7 +1304,7 @@ def _try_best_effort_sync():
12491304

12501305
try:
12511306
# Try to sync default refs (don't be verbose, don't fail on errors)
1252-
config = ModelSources.load()
1307+
config = ModelSourceConfig.load()
12531308
config.sync(verbose=False)
12541309
except Exception:
12551310
# Silently fail - user will get clear error when trying to use registry

modflow_devtools/models/__main__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from . import (
1919
_DEFAULT_CACHE,
20-
ModelSources,
20+
ModelSourceConfig,
2121
_try_best_effort_sync,
2222
)
2323

@@ -47,7 +47,7 @@ def _format_grid(items, prefix=""):
4747

4848
def cmd_sync(args):
4949
"""Sync command handler."""
50-
config = ModelSources.load()
50+
config = ModelSourceConfig.load()
5151

5252
# If a specific source is provided, sync just that source
5353
if args.source:
@@ -65,9 +65,9 @@ def cmd_sync(args):
6565
if source_obj is None:
6666
# If --repo is provided, create an ad-hoc source
6767
if args.repo:
68-
from . import ModelSource
68+
from . import ModelSourceRepo
6969

70-
source_obj = ModelSource(
70+
source_obj = ModelSourceRepo(
7171
repo=args.repo,
7272
name=args.source,
7373
refs=[args.ref] if args.ref else [],
@@ -111,7 +111,7 @@ def cmd_info(args):
111111
if os.environ.get("MODFLOW_DEVTOOLS_AUTO_SYNC", "").lower() in ("1", "true", "yes"):
112112
_try_best_effort_sync()
113113

114-
config = ModelSources.load()
114+
config = ModelSourceConfig.load()
115115
status = config.status
116116

117117
if not status:

modflow_devtools/programs/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,7 @@ def download_archive(
836836
if github_token:
837837
headers["Authorization"] = f"token {github_token}"
838838

839-
response = requests.get(url, headers=headers, stream=True, timeout=30) # type: ignore
839+
response = requests.get(url, headers=headers, stream=True, timeout=30)
840840
response.raise_for_status()
841841

842842
# Write to temporary file first

0 commit comments

Comments
 (0)