OmegaConf manages hierarchical YAML-based configuration with interpolation and merging. pip install omegaconf. Create: from omegaconf import OmegaConf; cfg = OmegaConf.create({"db": {"host": "localhost", "port": 5432}}). cfg.db.host → “localhost”. YAML: cfg = OmegaConf.load("config.yaml"). Merge: OmegaConf.merge(base_cfg, override_cfg) — deep merge, override wins. CLI: OmegaConf.from_dotlist(["db.host=prod.db", "debug=true"]). Interpolation: {"host": "db", "url": "postgres://${host}:5432/app"} → url resolves to “postgres://db:5432/app”. MISSING: from omegaconf import MISSING; class DBConfig: password: str = MISSING — raises if not supplied. Struct: @dataclass; class Config: debug: bool = False. cfg = OmegaConf.structured(Config). OmegaConf.is_missing(cfg, "password"). Readonly: OmegaConf.set_readonly(cfg, True) — raises on write. OmegaConf.is_readonly. to_yaml: OmegaConf.to_yaml(cfg) → YAML string. to_container: OmegaConf.to_container(cfg, resolve=True) → plain dict. Select: OmegaConf.select(cfg, "db.host", default="localhost"). Resolver: OmegaConf.register_new_resolver("env", lambda k: os.environ.get(k, "")). ${env:DB_HOST}. Hydra: @hydra.main(config_path="conf", config_name="config"). Claude Code generates OmegaConf config hierarchies, YAML loaders, and ML experiment configs.
CLAUDE.md for OmegaConf
## OmegaConf Stack
- Version: omegaconf >= 2.3 | pip install omegaconf
- Create: OmegaConf.create({"db": {"host": "localhost"}}) | OmegaConf.load("cfg.yaml")
- Access: cfg.db.host | cfg["db"]["host"] — dot and bracket access
- Merge: OmegaConf.merge(base, override) — deep merge, override wins
- Interpolation: "${db.host}" → value of db.host key
- Structured: @dataclass + OmegaConf.structured(Config) for typed configs
- MISSING: field has no value — accessing raises MissingMandatoryValue
OmegaConf Configuration Pipeline
# app/config.py — OmegaConf hierarchical config, merging, and structured types
from __future__ import annotations
import os
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
from omegaconf import MISSING, DictConfig, OmegaConf, open_dict
# ─────────────────────────────────────────────────────────────────────────────
# 1. Structured config dataclasses
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class DatabaseConfig:
host: str = "localhost"
port: int = 5432
name: str = "appdb"
user: str = "postgres"
password: str = MISSING # Required — must be supplied at runtime
@dataclass
class RedisConfig:
host: str = "localhost"
port: int = 6379
db: int = 0
ttl: int = 3600
@dataclass
class ServerConfig:
host: str = "0.0.0.0"
port: int = 8000
debug: bool = False
workers: int = 4
@dataclass
class AppConfig:
app_name: str = "MyApp"
environment: str = "development"
log_level: str = "INFO"
db: DatabaseConfig = field(default_factory=DatabaseConfig)
redis: RedisConfig = field(default_factory=RedisConfig)
server: ServerConfig = field(default_factory=ServerConfig)
# ─────────────────────────────────────────────────────────────────────────────
# 2. Config creation helpers
# ─────────────────────────────────────────────────────────────────────────────
def create_default_config() -> DictConfig:
"""Create a typed OmegaConf DictConfig from the AppConfig dataclass."""
return OmegaConf.structured(AppConfig)
def load_yaml(path: str | Path) -> DictConfig:
"""Load a YAML config file into a DictConfig."""
return OmegaConf.load(str(path))
def from_dotlist(overrides: list[str]) -> DictConfig:
"""
Create a DictConfig from a list of "key=value" override strings.
"db.host=prod.db", "server.debug=true", "db.port=5433"
"""
return OmegaConf.from_dotlist(overrides)
def from_env(prefix: str = "APP__") -> DictConfig:
"""
Build a DictConfig from environment variables with double-underscore nesting.
APP__DB__HOST → db.host
Strips the prefix, lowercases keys, and converts __ to dots.
"""
items = {}
for key, value in os.environ.items():
if key.upper().startswith(prefix.upper()):
stripped = key[len(prefix):]
nested_key = stripped.replace("__", ".").lower()
items[nested_key] = value
return OmegaConf.from_dotlist([f"{k}={v}" for k, v in items.items()])
# ─────────────────────────────────────────────────────────────────────────────
# 3. Merging and overriding
# ─────────────────────────────────────────────────────────────────────────────
def merge(*configs: DictConfig) -> DictConfig:
"""
Deep-merge multiple DictConfigs in order (later wins).
OmegaConf.merge(base, dev_overrides, cli_overrides)
"""
return OmegaConf.merge(*configs)
def load_with_overrides(
base_path: str | Path,
override_path: str | Path | None = None,
cli_overrides: list[str] | None = None,
) -> DictConfig:
"""
Load a base YAML, optionally merge an override YAML, then apply CLI overrides.
Priority: CLI > override_path > base_path
"""
cfg = load_yaml(base_path)
if override_path and Path(override_path).exists():
override = load_yaml(override_path)
cfg = OmegaConf.merge(cfg, override)
if cli_overrides:
cli = from_dotlist(cli_overrides)
cfg = OmegaConf.merge(cfg, cli)
return cfg
def apply_env_overrides(cfg: DictConfig, prefix: str = "APP__") -> DictConfig:
"""Apply environment variable overrides on top of an existing config."""
env_cfg = from_env(prefix=prefix)
return OmegaConf.merge(cfg, env_cfg)
# ─────────────────────────────────────────────────────────────────────────────
# 4. Access helpers
# ─────────────────────────────────────────────────────────────────────────────
def get(cfg: DictConfig, key: str, default: Any = None) -> Any:
"""
Safe dot-notation access with default.
get(cfg, "db.host", "localhost")
"""
return OmegaConf.select(cfg, key, default=default)
def is_missing(cfg: DictConfig, key: str) -> bool:
"""Return True if a key is set to MISSING."""
try:
return OmegaConf.is_missing(cfg, key)
except Exception:
return True
def check_required(cfg: DictConfig, required_keys: list[str]) -> list[str]:
"""Return a list of required keys that are MISSING or unresolved."""
missing = []
for key in required_keys:
if is_missing(cfg, key):
missing.append(key)
return missing
# ─────────────────────────────────────────────────────────────────────────────
# 5. Serialization
# ─────────────────────────────────────────────────────────────────────────────
def to_yaml(cfg: DictConfig, resolve: bool = False) -> str:
"""Serialize config to a YAML string."""
return OmegaConf.to_yaml(cfg, resolve=resolve)
def to_dict(cfg: DictConfig, resolve: bool = True) -> dict[str, Any]:
"""Convert DictConfig to a plain Python dict."""
return OmegaConf.to_container(cfg, resolve=resolve, throw_on_missing=False)
def save_yaml(cfg: DictConfig, path: str | Path, resolve: bool = False) -> None:
"""Write config to a YAML file."""
Path(path).write_text(to_yaml(cfg, resolve=resolve), encoding="utf-8")
# ─────────────────────────────────────────────────────────────────────────────
# 6. Readonly / freeze
# ─────────────────────────────────────────────────────────────────────────────
def freeze(cfg: DictConfig) -> DictConfig:
"""Return a read-only copy of the config. Raises on modification."""
OmegaConf.set_readonly(cfg, True)
return cfg
def unfreeze_context(cfg: DictConfig):
"""Context manager to temporarily allow writes to a readonly config."""
return open_dict(cfg)
# ─────────────────────────────────────────────────────────────────────────────
# 7. Custom resolvers
# ─────────────────────────────────────────────────────────────────────────────
def register_env_resolver() -> None:
"""
Register an ${env:VAR_NAME} interpolation resolver.
In YAML: url: "postgres://${env:DB_HOST}:5432/app"
"""
try:
OmegaConf.register_new_resolver(
"env",
lambda key, default="": os.environ.get(key, default),
replace=False,
)
except Exception:
pass # already registered
def register_path_resolver() -> None:
"""
Register a ${path:rel/path} resolver that makes the path absolute.
In YAML: output_dir: "${path:outputs}"
"""
try:
OmegaConf.register_new_resolver(
"path",
lambda rel: str(Path(rel).resolve()),
replace=False,
)
except Exception:
pass
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
# Register resolvers
register_env_resolver()
register_path_resolver()
print("=== Structured config ===")
cfg = create_default_config()
print(f" app_name: {cfg.app_name}")
print(f" db.host: {cfg.db.host}")
print(f" db.port: {cfg.db.port}")
print(f" db.password MISSING: {is_missing(cfg, 'db.password')}")
print("\n=== Merge (override) ===")
base = OmegaConf.create({
"db": {"host": "localhost", "port": 5432},
"debug": False,
"log_level": "INFO",
})
prod_override = OmegaConf.create({
"db": {"host": "prod.db.example.com"},
"log_level": "WARNING",
})
cli_override = from_dotlist(["debug=false", "db.port=5433"])
merged = merge(base, prod_override, cli_override)
print(f" db.host: {merged.db.host}")
print(f" db.port: {merged.db.port}")
print(f" log_level: {merged.log_level}")
print(f" debug: {merged.debug}")
print("\n=== Interpolation ===")
interp_cfg = OmegaConf.create({
"db_host": "mydb.example.com",
"db_port": 5432,
"db_url": "postgresql://${db_host}:${db_port}/app",
})
print(f" db_url: {interp_cfg.db_url}")
print("\n=== YAML output ===")
print(to_yaml(merged))
print("=== to_dict ===")
d = to_dict(merged)
for k, v in d.items():
print(f" {k}: {v!r}")
print("\n=== readonly ===")
frozen = freeze(OmegaConf.create({"x": 1}))
try:
frozen.x = 2
except Exception as e:
print(f" Got expected error: {type(e).__name__}: {e}")
with unfreeze_context(frozen):
frozen.x = 99
print(f" After unfreeze write: x={frozen.x}")
For the configparser alternative — Python’s configparser reads INI files into flat string sections with no type coercion, no nesting, no YAML, and no merging; OmegaConf reads YAML (or dict/dataclass), coerces types, supports nested keys like cfg.db.host, and merges configs with override semantics — OmegaConf is the right tool when your config is hierarchical and type-safe. For the hydra-core relationship — Hydra is built on top of OmegaConf and adds @hydra.main(), multi-run sweeps, config groups, and command-line override parsing; if you need just the config object (not the CLI launcher), OmegaConf alone is lighter and works outside of Hydra’s opinionated app structure. The Claude Skills 360 bundle includes OmegaConf skill sets covering OmegaConf.create(), OmegaConf.load() YAML, OmegaConf.merge() deep merge, @dataclass structured configs with MISSING, OmegaConf.select() safe access, OmegaConf.to_yaml()/to_container(), register_new_resolver() for ${env:KEY}, from_dotlist() CLI overrides, from_env() double-underscore env vars, load_with_overrides() multi-source loading, freeze()/open_dict() readonly management, and check_required() MISSING validation. Start with the free tier to try hierarchical configuration code generation.