Configuration management for Python projects with dataclass-based schemas
Project description
Clevis
Configuration management for Python projects with dataclass-based schemas
Clevis provides type-safe configuration management for Python applications:
- Dataclass schemas — Define config structure with Python dataclasses
- TOML support — Load from
.tomlfiles with automatic discovery - Env vars —
${VAR}interpolation (with envtoml/tomlev extras) - CLI generation — Auto-generate argparse from dataclass
- Layered config — User config < project config < CLI args
- Subcommands — Build CLI apps with multiple commands
- Dynamic registration — Plugin architecture with runtime field injection
- Security — File permission validation to protect credentials
Quick Start
Get running in 30 seconds:
# Install (Python 3.11+)
pip install clevis
# Or with environment variable support
pip install clevis[envtoml]
from dataclasses import dataclass
from clevis import get_config
@dataclass
class Config:
name: str = "MyApp"
debug: bool = False
port: int = 8080
# Load from ~/.myapp.toml, ./myapp.toml, and CLI args
config = get_config(Config, name="myapp")
print(config.name) # Access as attribute
print(config.debug) # Type-safe access
print(config.port) # Automatic type conversion
That's it! Create a myapp.toml file:
name = "Production App"
debug = true
[database]
host = "db.example.com"
port = 5432
Override with CLI:
python app.py --name "Custom App" --port 9000
Features
| Feature | Description | Example |
|---|---|---|
| Dataclass schemas | Type-safe configuration | @dataclass class Config: ... |
| TOML loading | Auto-discover config files | get_config(Config, name="myapp") |
| Environment vars | ${VAR} interpolation |
pip install clevis[envtoml] |
| CLI arguments | Auto-generate argparse | python app.py --database-host localhost |
| Boolean flags | --flag for True, --no-flag for False |
python app.py --debug / --no-debug |
| List append | Repeat --field val |
python app.py --packages pkg --packages c3 |
| Layered config | Defaults < User < Project < CLI | Priority-based merging |
| Nested configs | Hierarchical configuration | [database] host = "localhost" |
| Subcommands | Multi-command CLI apps | @configclass(cmd="build") |
| Factory pattern | Multi-module orchestration | get_factory(Config).prefix = "app1" |
| Dynamic registration | Plugin architecture | register_field(Parent, "plugin", PluginConfig) |
| Security | File permission validation | SecurityAction.REJECT (default) |
Installation
Choose your TOML parser based on needs:
| Extra | Features | Use When |
|---|---|---|
| (none) | Stdlib tomllib |
Python 3.11+, minimal deps |
tomli |
Pure Python TOML | Python 3.10 compatibility |
envtoml |
${VAR} interpolation |
Environment-based config |
tomlev |
`${VAR | default}` syntax |
# Python 3.11+ - no extras needed
pip install clevis
# Python 3.10
pip install clevis[tomli]
# Environment variable support
pip install clevis[envtoml]
Core Concepts
Configuration Priority
Values are merged in order (highest priority wins):
- CLI arguments —
--database-host localhost - Environment variables — Only when using envtoml/tomlev
- Project TOML —
./myapp.toml - User TOML —
~/.myapp.toml - Dataclass defaults — Default values in class definition
Security
Clevis validates file permissions by default:
from clevis import get_config, SecurityAction
# Default: reject insecure configurations
config = get_config(Config, name="myapp")
# Disable checks (for containers, testing)
config = get_config(
Config,
name="myapp",
security={"file_permissions": SecurityAction.DONT_CHECK}
)
# Log warnings instead of rejecting
config = get_config(
Config,
name="myapp",
security={"file_permissions": SecurityAction.LOG}
)
Custom Config Sources
Inject config from any source (env vars, databases, remote services) by appending a custom provider to the default cascade. Any zero-argument callable returning a dict works — a plain function is the simplest form:
from clevis import build_default_cascade, get_config
def env_provider() -> dict:
import os
return {"api_key": os.environ.get("API_KEY", "")}
cascade = build_default_cascade("myapp") + [env_provider]
config = get_config(Config, name="myapp", cascade=cascade)
Use a class with __call__ when the provider needs constructor parameters.
See docs/usage.rst for the full guide including security helpers for custom providers.
Examples
The examples/ directory contains 10 comprehensive examples:
| Example | Features Demonstrated | Run Command |
|---|---|---|
| main.py | Basic config, CLI args, TOML, security | uv run python main.py --help |
| nested.py | Nested dataclasses, TOML sections | uv run python nested.py --tool-settings-x 10 |
| validation.py | Custom validation, __post_init__ |
uv run python validation.py --server-url "http://localhost" |
| environment.py | ${VAR} interpolation, credentials |
export DB_HOST=localhost && uv run python environment.py |
| factory.py | Multi-module orchestration, prefixes | uv run python factory.py --app1-name "first" |
| commands.py | CLI subcommands, aliases | uv run python commands.py check --verbose |
| subcommands.py | TOML override with config parameter |
uv run python subcommands.py cli --server-url "https://api.example.com" |
| library_mode.py | Web framework integration, testing | uv run python library_mode.py |
| dynamic.py | Plugin architecture, register_field() |
uv run python dynamic.py --help |
| plugin.py | Practical plugin implementation | uv run python plugin.py --pkgq-timeout 60 |
See examples/README.md for detailed feature matrix and learning path.
Usage Patterns
For detailed usage patterns and API reference, see PACKAGE.md.
Basic Configuration
from dataclasses import dataclass
from clevis import get_config
@dataclass
class Config:
name: str = "MyApp"
debug: bool = False
config = get_config(Config, name="myapp")
CLI Subcommands
from clevis import configclass, get_cmd, get_config
@configclass(cmd="check", help="Run diagnostics", aliases=["c", "chk"])
class CheckConfig:
verbose: bool = False
fix: bool = False
@configclass(cmd="build", help="Build the project")
class BuildConfig:
output: str = "dist"
if __name__ == "__main__":
cmd = get_cmd()
if cmd == "check":
config = get_config(CheckConfig, project=False, user=False)
print(f"Checking with verbose={config.verbose}")
elif cmd == "build":
config = get_config(BuildConfig, project=False, user=False)
print(f"Building to {config.output}")
Dynamic Registration (Plugin Architecture)
from dataclasses import dataclass
from clevis import register_field, get_config
# Parent config (must NOT be frozen)
@dataclass
class ToolsConfig:
list: str = "default"
# Plugin config
@dataclass
class PkgqToolConfig:
enabled: bool = True
timeout: int = 30
# Register plugin field at runtime
register_field(ToolsConfig, "pkgq", PkgqToolConfig)
# Now ToolsConfig has a pkgq field
config = get_config(ToolsConfig, name="tools")
print(config.pkgq.enabled) # True
print(config.pkgq.timeout) # 30
Library Mode
from clevis import get_config
# Library mode - skip CLI parsing
config = get_config(Config, name="myapp", cli=False)
# Testing
def test_my_config():
config = get_config(
TestConfig,
user=False,
project=False,
args=[]
)
assert config.name == "default"
API Reference
For complete API documentation, see PACKAGE.md or docs/api.rst.
Key functions:
get_config(data_class, name, ...)— Load configuration from TOML and CLIconfigclass(cmd=None, help=None, ...)— Decorator for configuration classesregister_field(parent_class, field_name, field_type)— Register plugin fieldsget_factory(config_class)— Get Factory for multi-module appsget_cmd()— Get active subcommand name
Documentation
- Quick Start — This README
- Examples — examples/README.md with feature matrix
- Package Guide — PACKAGE.md comprehensive API and patterns
- API Reference — docs/api.rst or clevis.readthedocs.io
Testing
# Run tests
make test
# Run tests with coverage
make test-cov
# Run quality checks
make check
Contributing
# Clone the repository
git clone https://github.com/christophevg/clevis.git
cd clevis
# Create development environment
make env-dev
# Run tests
make test
# Run quality checks
make check
# Format code
make format
See Makefile for all available targets.
Acknowledgments
Clevis builds on excellent work from the Python community:
- tomllib — Python 3.11+ stdlib
- tomli — Pure Python TOML 1.0
- envtoml — Env var interpolation
- tomlev — Env vars with defaults
- dacite — Dict-to-dataclass conversion
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file clevis-0.7.0.tar.gz.
File metadata
- Download URL: clevis-0.7.0.tar.gz
- Upload date:
- Size: 281.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f8c75e7812d63ac816eecd0dc0252ae54aa5bf1e90eeaca03301ba04744e0220
|
|
| MD5 |
9236f2ddae92f6a016dc0759a936fe9a
|
|
| BLAKE2b-256 |
bfdcf249f731ac8ccf5bec66e1af19ca3ef6b18c90d7021cf2c9e897993314ca
|
File details
Details for the file clevis-0.7.0-py3-none-any.whl.
File metadata
- Download URL: clevis-0.7.0-py3-none-any.whl
- Upload date:
- Size: 35.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
99fc1364cc80e7d085a8a7b0319079c21696cb1ef1379d0b0b9115fc84f5f054
|
|
| MD5 |
4eda7b1c8315af3b9c3501a682d984e6
|
|
| BLAKE2b-256 |
a85fb56e6d221f14140b9310f61638f678e6694d7c5af3370851635f2da66dfd
|