Skip to main content

saneconfig

CI Docs PyPI

Dead-simple typed config loader for dataclasses:

  • defaults
  • TOML file(s)
  • environment variables

with great errors and provenance ("where did this value come from?").

Python 3.11+.

Core package has zero runtime dependencies; optional extras:

  • saneconfig[dotenv] for .env file support.

Install

pip install saneconfig
# optional .env support
pip install "saneconfig[dotenv]"

Quick start

from dataclasses import dataclass
from saneconfig import load, REQUIRED


@dataclass
class AppConfig:
    debug: bool = False
    port: int = 8080
    database_url: str = REQUIRED


cfg = load(
    AppConfig,
    env_prefix="APP",
    files=["config.toml", "config.local.toml"],
)

print(cfg.port)

Precedence (highest wins)

  1. CLI args (argv=True or argv=[...])
  2. Environment variables
  3. .env file values (when dotenv is enabled)
  4. TOML files (later overrides earlier)
  5. Dataclass defaults

TOML example

debug = true
port = 9000
database_url = "postgresql://user:pass@localhost:5432/mydb"

Nested dataclasses map to TOML tables:

[db]
host = "localhost"
port = 5432

Env var mapping

With env_prefix="APP":

  • APP_PORT=9000 -> port
  • APP_DEBUG=true -> debug
  • nested via __: APP_DB__HOST=localhost -> db.host

Booleans accept: true/false, yes/no, on/off, 1/0 (case-insensitive).

Lists in env vars use JSON:

APP_ALLOWED='["a","b"]'

CLI overrides

Enable CLI parsing by passing argv=True (uses sys.argv[1:]) or provide a list explicitly:

cfg = load(
    AppConfig,
    env_prefix="APP",
    argv=["--port=9001", "--db.host=db.internal"],
)

Supported form: --key=value with dotted nesting (--db.host=x).

Provenance / debugging

cfg, report = load(
    AppConfig,
    env_prefix="APP",
    files=["config.toml", "config.local.toml"],
    return_report=True,
)

print(report)

Example output:

database_url='postgresql://...' (env:APP_DATABASE_URL)
debug=False (default)
port=9000 (file:config.toml)

Schema docs

You can include richer docs using dataclass metadata:

from dataclasses import dataclass, field

@dataclass
class AppConfig:
    port: int = field(default=8080, metadata={"help": "HTTP listen port"})
from saneconfig import dump_schema
print(dump_schema(AppConfig, env_prefix="APP"))

dump_schema(...) includes a Help column using field(metadata={"help": ...}).

Errors that help

If a value cannot be converted:

ConfigError: port expected int but got "eighty"
  source: env:APP_PORT
  hint: Use a valid int value.

If required values are missing:

ConfigError: missing required configuration values:
  - database_url

Supported types (v0.1)

  • str, int, float, bool
  • Optional[T]
  • list[T] (TOML arrays or JSON arrays in env)
  • Literal[...]
  • nested dataclasses
  • pathlib.Path
  • dict[str, T] (TOML table or JSON object in env)

Non-goals (by design)

  • profiles/composition systems
  • interpolation DSLs
  • frameworks and global state

MIT Licensed.


A tiny example you can run

from dataclasses import dataclass
from typing import Literal, Optional

from saneconfig import REQUIRED, load


@dataclass
class DB:
    host: str = "localhost"
    port: int = 5432


@dataclass
class App:
    mode: Literal["dev", "prod"] = "dev"
    debug: bool = False
    port: int = 8080
    api_key: str = REQUIRED
    db: DB = DB()
    notes: Optional[str] = None


cfg, report = load(App, env_prefix="APP", files=["config.toml"], return_report=True)
print(cfg)
print(report)

Metadata

Release files for saneconfig 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for saneconfig 0.1.0
File Size Uploaded
saneconfig-0.1.0.tar.gz 14.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for saneconfig 0.1.0
File Interpreter ABI Platform
saneconfig-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 25.1 kB

Release files / saneconfig-0.1.0.tar.gz

Download URL saneconfig-0.1.0.tar.gz
Size 14.3 kB
Tags Source
SHA-256 checksum
How to use checksums
7ef26073be5317f419e9f865a08a55ff610bc6b2df1118ff1322c31bd77d4bcf
BLAKE2b-256 checksum
How to use checksums
d60f8c11ce6099d45eeccc93d98f6945197436008f4ca93279e0b93b128cee8a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.3

Release files / saneconfig-0.1.0-py3-none-any.whl

Download URL saneconfig-0.1.0-py3-none-any.whl
Size 10.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d7a00796a800c1c94fd91116e10044ce68a057701ec2d5361ad6474fcdbc56a6
BLAKE2b-256 checksum
How to use checksums
44eef23d0c0f75375f9e10fb9fc60b78170349f90705ea225235c992b6fc70ca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.3

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page