tomllib is the Python 3.11+ stdlib TOML parser (backport: pip install tomli). Python ≥3.11: import tomllib. Python <3.11: pip install tomli; import tomli as tomllib. Load file: with open("config.toml", "rb") as f: cfg = tomllib.load(f) — binary mode required. Load string: cfg = tomllib.loads('[server]\nport = 8080'). TOMLDecodeError: try: tomllib.loads(s) except tomllib.TOMLDecodeError as e: .... Nested: cfg["server"]["host"]. Array: cfg["servers"] → list. Inline table: {host="localhost",port=5432} → dict. Date: cfg["date"] → datetime.date. Datetime: cfg["created"] → datetime.datetime. Bool: cfg["debug"] → True/False. Int: cfg["port"] → 8080. Float: cfg["ratio"] → 0.95. Multi-line string: """...""". Write TOML: pip install tomli_w; import tomli_w; tomli_w.dumps({"key": "val"}). tomli_w.dump(obj, file). pyproject.toml: with open("pyproject.toml","rb") as f: meta = tomllib.load(f); name=meta["project"]["name"]. env override: os.environ.get("HOST") or cfg["server"]["host"]. Merge: {**defaults, **cfg}. validate: combine with pydantic or dataclasses for typed config. Claude Code generates tomllib config loaders, pyproject.toml readers, typed config dataclasses, and environment-layered settings.
CLAUDE.md for tomllib
## tomllib Stack
- Python >= 3.11: import tomllib | Python < 3.11: pip install tomli; import tomli as tomllib
- Load file: with open("cfg.toml", "rb") as f: cfg = tomllib.load(f) # binary mode
- Load str: cfg = tomllib.loads(toml_string)
- Write: pip install tomli_w; import tomli_w; tomli_w.dumps(obj)
- Error: except tomllib.TOMLDecodeError as e: print(e)
- Pattern: load → env override → validate with dataclass/pydantic
tomllib Configuration Pipeline
# app/config.py — tomllib load, merge, env override, typed dataclass, pyproject reader
from __future__ import annotations
import os
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
# Python 3.11+ stdlib — backport for older versions
if sys.version_info >= (3, 11):
import tomllib
else:
try:
import tomli as tomllib # pip install tomli
except ImportError as exc:
raise ImportError("Install tomli for Python < 3.11: pip install tomli") from exc
# Optional write support
try:
import tomli_w
_HAS_TOMLI_W = True
except ImportError:
_HAS_TOMLI_W = False
# ─────────────────────────────────────────────────────────────────────────────
# 1. Load helpers
# ─────────────────────────────────────────────────────────────────────────────
def load_file(path: str | Path) -> dict:
"""
Load a TOML file. Binary mode is required by tomllib.
Example:
cfg = load_file("config.toml")
host = cfg["server"]["host"]
"""
with open(path, "rb") as f:
return tomllib.load(f)
def load_string(text: str) -> dict:
"""
Parse a TOML string.
Example:
cfg = load_string('[db]\\nhost = "localhost"\\nport = 5432')
"""
return tomllib.loads(text)
def try_load(path: str | Path, default: dict | None = None) -> dict:
"""
Load a TOML file; return default dict on missing file or parse error.
Example:
cfg = try_load("local.toml", default={})
"""
try:
return load_file(path)
except FileNotFoundError:
return default if default is not None else {}
except tomllib.TOMLDecodeError as e:
raise ValueError(f"Invalid TOML in {path}: {e}") from e
# ─────────────────────────────────────────────────────────────────────────────
# 2. Merge and layering
# ─────────────────────────────────────────────────────────────────────────────
def deep_merge(base: dict, override: dict) -> dict:
"""
Recursively merge override into base (override wins on conflicts).
Example:
defaults = {"server": {"host": "0.0.0.0", "port": 8080, "workers": 4}}
local = {"server": {"host": "localhost"}}
merged = deep_merge(defaults, local)
# {"server": {"host": "localhost", "port": 8080, "workers": 4}}
"""
result = base.copy()
for key, val in override.items():
if key in result and isinstance(result[key], dict) and isinstance(val, dict):
result[key] = deep_merge(result[key], val)
else:
result[key] = val
return result
def load_layered(
*paths: str | Path,
ignore_missing: bool = True,
) -> dict:
"""
Load multiple TOML files in order, merging each on top of the previous.
Later files override earlier ones.
Example:
cfg = load_layered(
"config.default.toml",
"config.toml",
f"config.{os.getenv('ENV','dev')}.toml",
)
"""
result: dict = {}
for path in paths:
try:
data = load_file(path)
result = deep_merge(result, data)
except FileNotFoundError:
if not ignore_missing:
raise
return result
# ─────────────────────────────────────────────────────────────────────────────
# 3. Environment variable overlay
# ─────────────────────────────────────────────────────────────────────────────
def apply_env_overrides(
cfg: dict,
prefix: str = "APP_",
separator: str = "__",
) -> dict:
"""
Override cfg values from environment variables.
Variable names: PREFIX__SECTION__KEY (mapped to nested cfg keys)
Example:
os.environ["APP__SERVER__PORT"] = "9090"
cfg = apply_env_overrides(cfg, prefix="APP__")
cfg["server"]["port"] # "9090" (as string)
"""
result = {k: v for k, v in cfg.items()}
for env_key, env_val in os.environ.items():
if not env_key.startswith(prefix):
continue
parts = env_key[len(prefix):].lower().split(separator)
target = result
for part in parts[:-1]:
if part not in target:
target[part] = {}
target = target[part]
leaf_key = parts[-1]
# Try to cast numeric/bool values
if env_val.lower() in ("true", "yes", "1"):
target[leaf_key] = True
elif env_val.lower() in ("false", "no", "0"):
target[leaf_key] = False
else:
try:
target[leaf_key] = int(env_val)
except ValueError:
try:
target[leaf_key] = float(env_val)
except ValueError:
target[leaf_key] = env_val
return result
# ─────────────────────────────────────────────────────────────────────────────
# 4. Typed config with dataclasses
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class ServerConfig:
host: str = "0.0.0.0"
port: int = 8080
workers: int = 4
debug: bool = False
reload: bool = False
@dataclass
class DatabaseConfig:
url: str = "sqlite:///app.db"
pool_size: int = 5
pool_timeout: float = 30.0
echo: bool = False
@dataclass
class LoggingConfig:
level: str = "INFO"
format: str = "json"
file: str | None = None
@dataclass
class AppConfig:
server: ServerConfig = field(default_factory=ServerConfig)
database: DatabaseConfig = field(default_factory=DatabaseConfig)
logging: LoggingConfig = field(default_factory=LoggingConfig)
debug: bool = False
version: str = "0.0.0"
def dict_to_dataclass(data: dict, cls):
"""
Populate a dataclass from a dict, recursively for nested dataclasses.
Extra keys are silently ignored.
Example:
cfg = dict_to_dataclass(raw_config, AppConfig)
"""
import dataclasses
if not dataclasses.is_dataclass(cls):
return data
kwargs = {}
for f in dataclasses.fields(cls):
if f.name not in data:
continue
val = data[f.name]
if dataclasses.is_dataclass(f.type) and isinstance(val, dict):
val = dict_to_dataclass(val, f.type)
elif isinstance(f.default_factory, type) and dataclasses.is_dataclass(f.default_factory): # type: ignore
pass
kwargs[f.name] = val
return cls(**kwargs)
def load_app_config(
path: str | Path = "config.toml",
env_prefix: str = "APP__",
) -> AppConfig:
"""
Load and validate app configuration from TOML + environment.
Example:
cfg = load_app_config("config.toml")
print(cfg.server.port)
print(cfg.database.url)
"""
raw = try_load(path, default={})
raw = apply_env_overrides(raw, prefix=env_prefix)
return dict_to_dataclass(raw, AppConfig)
# ─────────────────────────────────────────────────────────────────────────────
# 5. pyproject.toml reader
# ─────────────────────────────────────────────────────────────────────────────
def read_pyproject(path: str | Path = "pyproject.toml") -> dict:
"""
Read a pyproject.toml and return its contents.
Example:
meta = read_pyproject()
name = meta.get("project", {}).get("name", "unknown")
deps = meta.get("project", {}).get("dependencies", [])
"""
return try_load(path, default={})
def get_project_meta(pyproject: str | Path = "pyproject.toml") -> dict:
"""
Extract common project metadata from pyproject.toml.
Example:
meta = get_project_meta()
print(f"{meta['name']} v{meta['version']}")
"""
data = read_pyproject(pyproject)
project = data.get("project", {})
tool = data.get("tool", {})
return {
"name": project.get("name", ""),
"version": project.get("version", ""),
"description": project.get("description", ""),
"authors": project.get("authors", []),
"requires_python": project.get("requires-python", ""),
"dependencies": project.get("dependencies", []),
"optional_deps": project.get("optional-dependencies", {}),
"scripts": project.get("scripts", {}),
"build_backend": data.get("build-system", {}).get("build-backend", ""),
}
# ─────────────────────────────────────────────────────────────────────────────
# 6. Write support (requires tomli_w)
# ─────────────────────────────────────────────────────────────────────────────
def dump_toml(obj: dict) -> str:
"""
Serialize a dict to TOML string.
Requires: pip install tomli_w
Example:
text = dump_toml({"server": {"host": "localhost", "port": 8080}})
"""
if not _HAS_TOMLI_W:
raise ImportError("Install tomli_w to write TOML: pip install tomli_w")
return tomli_w.dumps(obj)
def write_toml(obj: dict, path: str | Path) -> Path:
"""Write dict to a TOML file."""
if not _HAS_TOMLI_W:
raise ImportError("Install tomli_w to write TOML: pip install tomli_w")
p = Path(path)
p.parent.mkdir(parents=True, exist_ok=True)
with open(p, "wb") as f:
tomli_w.dump(obj, f)
return p
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
SAMPLE_TOML = """
[server]
host = "0.0.0.0"
port = 8080
workers = 4
debug = false
[database]
url = "postgresql://user:pass@localhost/mydb"
pool_size = 10
[logging]
level = "INFO"
format = "json"
version = "1.2.3"
"""
print("=== load_string ===")
raw = load_string(SAMPLE_TOML)
print(f" server.port: {raw['server']['port']}")
print(f" database.url: {raw['database']['url']}")
print(f" version: {raw['version']}")
print("\n=== deep_merge ===")
defaults = {"server": {"host": "0.0.0.0", "port": 8080, "workers": 4}}
override = {"server": {"host": "localhost", "debug": True}}
merged = deep_merge(defaults, override)
print(f" Merged server: {merged['server']}")
print("\n=== env overrides ===")
os.environ["APP__SERVER__PORT"] = "9090"
os.environ["APP__DEBUG"] = "true"
overridden = apply_env_overrides(raw, prefix="APP__")
print(f" server.port after env: {overridden['server']['port']}")
print(f" debug after env: {overridden.get('debug')}")
print("\n=== typed config (dict_to_dataclass) ===")
typed = dict_to_dataclass(raw, AppConfig)
print(f" AppConfig.server.port: {typed.server.port}")
print(f" AppConfig.database.url: {typed.database.url}")
print(f" AppConfig.logging.level: {typed.logging.level}")
print(f" AppConfig.version: {typed.version}")
if _HAS_TOMLI_W:
print("\n=== dump_toml ===")
text = dump_toml({"project": {"name": "myapp", "version": "1.0.0"}})
print(f" {text.strip()}")
else:
print("\n(install tomli_w for write: pip install tomli_w)")
For the configparser stdlib alternative — configparser handles INI-style files (key=value under [section]) and is built into Python; TOML supports typed values (integers, booleans, datetimes, arrays, nested tables) and is the standard for pyproject.toml and modern Python project config — use configparser for simple legacy INI files, TOML for new configuration files where type fidelity matters. For the pydantic-settings alternative — pydantic-settings loads configuration from environment variables, dotenv files, and arbitrary sources with full Pydantic v2 type validation and coercion; tomllib is a pure TOML parser with no validation layer — combine tomllib with a pydantic Settings model by loading the TOML file then feeding the dict to Settings(**cfg) for full type validation. The Claude Skills 360 bundle includes tomllib skill sets covering load_file()/load_string()/try_load(), deep_merge()/load_layered() multi-file layering, apply_env_overrides() with prefix/separator, dict_to_dataclass() typed dataclass binding, AppConfig/ServerConfig/DatabaseConfig dataclasses, load_app_config() one-call setup, read_pyproject()/get_project_meta(), and dump_toml()/write_toml() with tomli_w. Start with the free tier to try TOML configuration management code generation.