Skip to main content

Configuration management for Python projects with dataclass-based schemas

Project description

Clevis

PyPI Python CI Coverage License Agentic PACKAGE.md

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 .toml files
  • Env vars${VAR} interpolation (with envtoml/tomlev)
  • CLI generation — Auto-generate argparse from dataclass
  • Layered config — User config < project config < CLI args

About the Name

A clevis is a U-shaped mechanical fastener that connects components while allowing pivoting. It's used in everything from agricultural equipment to aerospace control systems — a simple, robust connector that provides flexibility without compromising strength.

This library follows the same principle: it connects multiple configuration sources (TOML files, environment variables, CLI arguments) into a single, cohesive interface. Just as a mechanical clevis allows articulation, Clevis allows your configuration to flex and adapt — user-level defaults, project-level settings, and runtime overrides all pivot around a single dataclass schema.

Quick Start

# Install (Python 3.11+)
pip install clevis

# Or with env var support
pip install clevis[envtoml]
from dataclasses import dataclass
from clevis import get_config

@dataclass
class Config:
    name: str = "MyApp"
    debug: bool = False

config = get_config(Config, name="app")

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 Env vars with defaults
# Python 3.11+ - no extras needed
pip install clevis

# Python 3.10
pip install clevis[tomli]

# Environment variable support
pip install clevis[envtoml]

Usage

Define Your Config

from dataclasses import dataclass, field

@dataclass
class DatabaseConfig:
    host: str = "localhost"
    port: int = 5432
    user: str | None = None
    password: str | None = None

@dataclass
class AppConfig:
    name: str = "MyApp"
    debug: bool = False
    database: DatabaseConfig = field(default_factory=DatabaseConfig)

Load Configuration

from clevis import get_config

# Load from ~/.myapp.toml and ./myapp.toml
config = get_config(AppConfig, name="myapp")

Configuration layers (lowest to highest priority):

  1. Dataclass defaults
  2. User-level TOML~/.{name}.toml
  3. Project-level TOML./{name}.toml
  4. CLI arguments--database-host, --debug

TOML Files

Create myapp.toml:

name = "Production App"
debug = true

[database]
host = "db.example.com"
port = 5432

With env var support (clevis[envtoml] or clevis[tomlev]):

[database]
password = "${DB_PASSWORD}"        # envtoml
host = "${DB_HOST|localhost}"       # tomlev (with default)

CLI Arguments

Clevis auto-generates CLI arguments:

python app.py --database-host localhost
python app.py --database-port 5432
python app.py --debug

Nested dataclasses become dashed arguments: database.host--database-host

Testing

# Run tests
make test

# Run with coverage
make test-cov

Library Integration

When using Clevis as a library (not a CLI app), you can disable CLI parsing:

from clevis import get_config

# Library mode - skip sys.argv parsing
config = get_config(Config, name="myapp", cli=False)

# Programmatic control with explicit args
config = get_config(Config, name="myapp", cli=False, args=["--debug"])

# Testing
def test_my_config():
    config = get_config(TestConfig, cli=False, user=False, project=False)
    assert config.name == "default"

Why cli=False?

Using cli=False instead of args=[]:

  • Clear Intent: Explicitly signals library usage
  • Better Errors: Error messages omit CLI suggestions when not applicable
  • No Overhead: Skips argparse parser creation entirely

API Reference

get_config(data_class, name="project", user=True, project=True, cli=True, args=None)

Load configuration from TOML files and CLI arguments.

Parameters:

  • data_class — The dataclass type to populate
  • name — Config file name (without .toml)
  • user — Load user-level config (~/.{name}.toml)
  • project — Load project-level config (./{name}.toml)
  • cli — Parse CLI arguments from sys.argv (default: True)
  • args — CLI arguments (defaults to sys.argv[1:] when cli=True)
  • security — Security check configuration (default: maximally strict)

Returns: Instance of the dataclass with merged configuration

Raises:

  • ConfigError — Missing required fields or wrong types
  • SecurityError — Security check failed (when action="reject")
  • ImportError — No TOML parser available

Security

Clevis validates configuration file security by default:

  • File permissions — Rejects files readable by group/other (mode 0o644, 0o755, etc.)
  • Directory permissions — Rejects files in world-writable directories

This protects against symlink attacks and accidental credential exposure.

Default Behavior (Maximally Strict)

# Default: reject insecure configurations
config = get_config(Config, name="myapp")

Disable Checks

For trusted environments (containers, development):

from clevis import get_config, SecurityAction

# Skip all security checks
config = get_config(
    Config,
    name="myapp",
    security={
        "file_permissions": SecurityAction.DONT_CHECK,
        "directory_permissions": SecurityAction.DONT_CHECK
    }
)

Log Warnings

For development with monitoring:

from clevis import get_config, SecurityAction

# Log warnings instead of rejecting
config = get_config(
    Config,
    name="myapp",
    security={
        "file_permissions": SecurityAction.LOG,
        "directory_permissions": SecurityAction.LOG
    }
)

Fine-Grained Control

from clevis import get_config, SecurityAction

# Check file permissions, ignore directory
config = get_config(
    Config,
    name="myapp",
    security={
        "file_permissions": SecurityAction.REJECT,
        "directory_permissions": SecurityAction.DONT_CHECK
    }
)

Security Actions

Action Behavior
SecurityAction.DONT_CHECK Skip validation
SecurityAction.LOG Log warning, continue
SecurityAction.REJECT Raise SecurityError (default)

Fixing Security Issues

File permissions:

# Secure: owner read/write only
chmod 600 ~/.myapp.toml

Directory permissions:

# Move config from world-writable location
mv /tmp/myapp.toml ~/.myapp.toml

Trusted Locations

  • User's home directory (~/.myapp.toml) — directory check is skipped
  • Non-existent files — all checks are skipped

Error Messages

Clevis provides helpful, actionable errors:

When using CLI (default):

======================================================================
Configuration Error
======================================================================

Field: database.host
Issue: Required field has no value

Provide this value in one of these ways:

  1. Project config: ./project.toml
     [database]
     host = "your_value"

  2. User config: ~/.project.toml
     (same format as above)

  3. CLI argument: --database-host <value>

======================================================================

When using library mode (cli=False):

======================================================================
Configuration Error
======================================================================

Field: database.host
Issue: Required field has no value

Provide this value in one of these ways:

  1. Project config: ./project.toml
     [database]
     host = "your_value"

  2. User config: ~/.project.toml
     (same format as above)

======================================================================

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

clevis-0.3.1.tar.gz (168.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

clevis-0.3.1-py3-none-any.whl (16.6 kB view details)

Uploaded Python 3

File details

Details for the file clevis-0.3.1.tar.gz.

File metadata

  • Download URL: clevis-0.3.1.tar.gz
  • Upload date:
  • Size: 168.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for clevis-0.3.1.tar.gz
Algorithm Hash digest
SHA256 36b26bb0220f48210d2f9e8aa72388dea4c3b612dbac35d8a5414e8cd1fa9c92
MD5 35d3c46630869199cad075ff2668f5f8
BLAKE2b-256 f987d18101b0a6644d3715e7f5fdd829988fce8302971dfede53b71f95c3ebdf

See more details on using hashes here.

File details

Details for the file clevis-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: clevis-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 16.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for clevis-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c4c60f3b9f40fd8b88199cadf98bb10efba139ba2790bbc7f95f4c5e250ff2ed
MD5 2bc42053b2f4849d11a47ce0e4cf19b0
BLAKE2b-256 007deb25735374fb50ad6e6d4d74106881bcf1dfbe3e85f7a6277e45343fd945

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page