Programs API
Experimental API Warning
This API is experimental and may change or be removed in future versions without following normal deprecation procedures. Use at your own risk.
When importing this module programmatically, you will see a
FutureWarning. To suppress this warning:import warnings warnings.filterwarnings('ignore', message='.*modflow_devtools.programs.*experimental.*')The
mf programsCLI command is stable and does not trigger warnings.
The modflow_devtools.programs module provides programmatic access to MODFLOW and related programs in the MODFLOW ecosystem. It can be used with MODFLOW organization releases or custom program repositories.
This module builds on Pooch for file fetching and caching. While it leverages Pooch’s capabilities, it provides an independent layer with:
Registration, discovery and synchronization
Installation and version management
Platform-specific binary handling
Program registries can be synchronized from remote sources on demand. The user or developer can inspect and install programs published by the MODFLOW organization, from a personal fork, or from custom repositories.
Overview
The Programs API provides:
Program registration: Index local or remote program repositories
Program discovery: Browse available programs and versions
Program installation: Install pre-built binaries for your platform
Version management: Install multiple versions side-by-side and switch between them
Program metadata is provided by registries which are published by program repositories. On first use, modflow-devtools automatically attempts to sync these registries.
A program registry contains metadata for available programs including:
Program name and description
Available versions
Platform-specific distributions (binaries and assets)
Usage
Syncing registries
Registries can be manually synchronized:
from modflow_devtools.programs import ProgramSourceConfig
config = ProgramSourceConfig.load()
# Sync all configured sources
results = config.sync(verbose=True)
# Sync specific source
results = config.sync(source="modflow6", verbose=True)
Or via CLI (both forms are equivalent):
# Using the mf command
mf programs sync
mf programs sync --source modflow6
mf programs sync --force # Force re-download of registry metadata
# Or using the module form
python -m modflow_devtools.programs sync
python -m modflow_devtools.programs sync --source modflow6
python -m modflow_devtools.programs sync --force # Force re-download of registry metadata
Note: The --force flag on sync forces re-downloading of registry metadata even if already cached. This is separate from installation - see the “Force semantics” section below.
Inspecting available programs
from modflow_devtools.programs import ProgramSourceConfig
config = ProgramSourceConfig.load()
# Check sync status
status = config.status
for source_name, source_status in status.items():
print(f"{source_name}: {source_status.cached_refs}")
Or by CLI (both forms are equivalent):
# Using the mf command
mf programs info # Show sync status
mf programs list # Show program summary
mf programs list --verbose # Full list with details
mf programs list --source modflow6 --verbose # Filter by source
# Or using the module form
python -m modflow_devtools.programs info # Show sync status
python -m modflow_devtools.programs list # Show program summary
python -m modflow_devtools.programs list --verbose # Full list with details
python -m modflow_devtools.programs list --source modflow6 --verbose # Filter by source
Installing a program
from modflow_devtools.programs import install_program
# Install latest available version
paths = install_program("mf6", verbose=True)
# Install specific version
paths = install_program("mf6", version="6.6.3", verbose=True)
# Install to custom directory
paths = install_program("mf6", version="6.6.3", bindir="/usr/local/bin")
Or via CLI (both forms are equivalent):
# Using the mf command
mf programs install mf6
mf programs install mf6@6.6.3
mf programs install mf6@6.6.3 --bindir /usr/local/bin
# Or using the module form
python -m modflow_devtools.programs install mf6
python -m modflow_devtools.programs install mf6@6.6.3
python -m modflow_devtools.programs install mf6@6.6.3 --bindir /usr/local/bin
Finding installed programs
from modflow_devtools.programs import get_executable, list_installed
# Get path to installed executable
mf6_path = get_executable("mf6")
# Get specific version
mf6_path = get_executable("mf6", version="6.6.3")
# List all installed programs
installed = list_installed()
for program_name, installations in installed.items():
for inst in installations:
print(f"{program_name} {inst.version} in {inst.bindir}")
Or by CLI (both forms are equivalent):
# Using the mf command
mf programs history
mf programs history mf6 --verbose
# Or using the module form
python -m modflow_devtools.programs history
python -m modflow_devtools.programs history mf6 --verbose
Version management
Multiple versions can be installed side-by-side. Switch between them using select:
from modflow_devtools.programs import install_program, select_version
# Install multiple versions
install_program("mf6", version="6.6.3")
install_program("mf6", version="6.5.0")
# Switch active version
select_version("mf6", version="6.5.0")
Or by CLI (both forms are equivalent):
# Using the mf command
mf programs install mf6@6.6.3
mf programs install mf6@6.5.0
# Version switching not yet implemented - use Python API
# Or using the module form
python -m modflow_devtools.programs install mf6@6.6.3
python -m modflow_devtools.programs install mf6@6.5.0
Using the default manager
The module provides explicit access to the default manager used by install_program() etc.
from modflow_devtools.programs import _DEFAULT_MANAGER
# Install programs
paths = _DEFAULT_MANAGER.install("mf6", version="6.6.3", verbose=True)
# Switch versions
_DEFAULT_MANAGER.select("mf6", version="6.5.0", verbose=True)
# Get executable path
mf6_path = _DEFAULT_MANAGER.get_executable("mf6")
# List installed programs
installed = _DEFAULT_MANAGER.list_installed()
# Uninstall specific version
_DEFAULT_MANAGER.uninstall("mf6", version="6.5.0")
# Uninstall all versions
_DEFAULT_MANAGER.uninstall("mf6", all_versions=True)
Customizing program sources
Create a user config file to add custom sources or override defaults:
Windows:
%APPDATA%/modflow-devtools/programs.tomlmacOS:
~/Library/Application Support/modflow-devtools/programs.tomlLinux:
~/.config/modflow-devtools/programs.toml
Example user config:
[sources.modflow6]
repo = "myusername/modflow6" # Use a fork for testing
refs = ["develop"]
The user config is automatically merged with the bundled config, allowing you to test against forks or add private repositories.
Working with registries
Access cached registry data directly:
from modflow_devtools.programs import _DEFAULT_CACHE, ProgramSourceConfig
config = ProgramSourceConfig.load()
# Check sync status
status = config.status
for source_name, source_status in status.items():
print(f"{source_name}: {source_status.cached_refs}")
# Load cached registry
registry = _DEFAULT_CACHE.load("modflow6", "6.6.3")
if registry:
for program_name, metadata in registry.programs.items():
print(f"{program_name} {metadata.version}")
print(f" Description: {metadata.description}")
print(f" Distributions: {[d.name for d in metadata.dists]}")
Program Addressing
Programs are addressed using the format: {program}@{version}.
Examples:
mf6@6.6.3- MODFLOW 6 version 6.6.3zbud6@6.6.3- MODFLOW 6 Zonebudget version 6.6.3mp7@7.2.001- MODPATH 7 version 7.2.001
Platform Support
The Programs API automatically detects your platform and downloads the appropriate binaries:
linux: Linux x86_64
mac: macOS ARM64 (Apple Silicon)
win64: Windows 64-bit
Programs must provide pre-built binaries for supported platforms. Building from source is not supported—program repositories are responsible for releasing platform-specific binaries.
Cache Management
Downloaded archives and installed binaries are cached locally:
Registries:
~/.cache/modflow-devtools/programs/registries/{source}/{ref}/Archives:
~/.cache/modflow-devtools/programs/archives/{program}/{version}/{platform}/Binaries:
~/.cache/modflow-devtools/programs/binaries/{program}/{version}/{platform}/Metadata:
~/.cache/modflow-devtools/programs/installations/{program}.json
The cache enables:
Fast re-installation without re-downloading
Efficient version switching
Offline access to previously installed programs
Force Semantics
The --force flag has different meanings depending on the command:
sync --force: Forces re-downloading of registry metadata from GitHub
Re-fetches
programs.tomleven if already cachedUse when registry files have been updated on GitHub
Does not affect installed programs or archives
install --force: Forces re-installation of program binaries
Re-extracts from cached archive and re-copies to installation directory
Does not re-sync registry metadata (use
sync --forcefirst if needed)Use when installation is corrupted or you want to reinstall to a different location
Works offline if archive is already cached
Common workflows:
# Update to latest registry and install
mf programs sync --force
mf programs install mf6
# Repair broken installation (offline-friendly)
mf programs install mf6 --force
# Fresh install with latest metadata
mf programs sync --force
mf programs install mf6 --force
Automatic Synchronization
Auto-sync is opt-in (experimental). To enable:
export MODFLOW_DEVTOOLS_AUTO_SYNC=1 # or "true" or "yes"
When enabled, modflow-devtools attempts to sync registries:
On first access (best-effort, fails silently on network errors)
Before installation
Before listing available programs
Then manually sync when needed:
mf programs sync
# Or: python -m modflow_devtools.programs sync
Repository Integration
Program repositories publish registry files (programs.toml) describing available programs and platform-specific distributions.
Registry Generation
The make_registry tool generates registry files from local or remote assets.
From local assets (typical CI usage):
python -m modflow_devtools.programs.make_registry \
--dists *.zip \
--programs mf6 zbud6 libmf6 mf5to6 \
--version 6.6.3 \
--repo MODFLOW-ORG/modflow6 \
--compute-hashes \
--output programs.toml
From existing GitHub release:
python -m modflow_devtools.programs.make_registry \
--repo MODFLOW-ORG/modflow6 \
--version 6.6.3 \
--programs mf6 zbud6 libmf6 mf5to6 \
--compute-hashes \
--output programs.toml
Publishing Registries
Registry files are published as GitHub release assets alongside binary distributions.
For instance, to publish a registry in a GitHub Actions workflow:
- name: Generate program registry
run: |
python -m modflow_devtools.programs.make_registry \
--dists *.zip \
--programs mf6 zbud6 libmf6 mf5to6 \
--version ${{ github.ref_name }} \
--repo ${{ github.repository }} \
--compute-hashes \
--output programs.toml
- name: Upload registry to release
uses: softprops/action-gh-release@v1
with:
files: programs.toml
Registry Format
The generated programs.toml file contains:
Program metadata (description, license)
Platform-specific distributions (linux, mac, win64)
Asset filenames and SHA256 hashes
Executable paths within archives
See the developer documentation for detailed registry format specifications.
Relationship to pymake and get-modflow
The Programs API is designed to eventually supersede:
pymake’s program database: Registry responsibilities are delegated to program repositories
flopy’s get-modflow: Installation patterns adapted and enhanced for multi-version support
The Programs API provides:
Decoupled releases (programs release independently of devtools)
Multiple versions side-by-side
Unified cache structure
Comprehensive installation tracking
Fast version switching