Skip to main content

Lightweight layered configuration loader with environment-aware overrides.

Project description

LiteConf

PyPI version Python versions License: MIT Build codecov

liteconf is a lightweight configuration loader focused on deterministic override rules, explicit layering, and ergonomic access patterns. It embraces boring tooling (plain YAML/JSON/TOML) while solving the painful parts typical projects hit:

  • predictable ordering across base/, local/, environment, and runtime overrides
  • structure-preserving deep merge (lists replace, dicts merge)
  • attribute and dict access via a single ConfigView
  • environment interpolation with type coercion
  • opt-in validation hooks without mandating a heavyweight framework

Why another config loader?

Existing Python options shine in specific niches but often trade simplicity for power:

  • dynaconf, hydra, omegaconf ship large abstraction layers, CLIs, or plugin registries
  • pydantic-settings and environs center on .env files and type validation, not layered YAML
  • configparser and ConfigObj struggle with nested structures

liteconf keeps the learning curve flat while covering the 90% case of layered application configs.

Install

pip install liteconf

Quick Start (Practical)

Consider this minimal layout (two folders + one JSON):

conf/
  base/
    service.yml
  local/
    service.yml
extra.json

conf/base/service.yml

service:
  url: https://api.base
  retries: 2
  features:
    cache: false
    logging: warn   # defined only in base

conf/local/service.yml

service:
  url: https://api.local          # overrides base
  retries: 3                      # overrides base; later overridden by extra.json
  features:
    cache: true                   # overrides base
    tracing: true                 # added by local
  webhook: "${SERVICE_WEBHOOK:https://hooks.local/notify}"  # env placeholder with default

extra.json

{
  "service": {
    "retries": 5,                 
    "timeout": 10,                
    "endpoint": { "health": "/health" }  
  }
}

Load everything with explicit source order (later wins):

from pathlib import Path
from liteconf import ConfigManager, DirectorySource, FileSource, EnvSource

manager = ConfigManager([
    DirectorySource(Path("conf/base"), optional=False),
    DirectorySource(Path("conf/local"), optional=True),
    FileSource(Path("extra.json"), optional=True),
    # Optional: environment overlay, e.g. APP__SERVICE__TIMEOUT=30
    EnvSource(prefix="APP", delimiter="__", infer_types=True),
])

cfg = manager.load()

# Value defined only in base
assert cfg.get("service.features.logging") == "warn"

# Values overridden by local
assert cfg.service.url == "https://api.local"
assert cfg.service.features.cache is True

# Value added by local
assert cfg.get("service.features.tracing") is True

# Overridden by local, then by extra.json (final)
assert cfg.service.retries == 5  # base=2 -> local=3 -> extra.json=5

# Values provided only by extra.json
assert cfg.service.timeout == 10
assert cfg.service.endpoint.health == "/health"

# You can use attribute-style or dotted lookups interchangeably
assert cfg.service.url == cfg.get("service.url")

# Save a copy if useful for debugging
cfg.save("debug_config.yml")

Notes:

  • The filename forms the first key: service.yml becomes top-level key service.
  • Attribute access (cfg.service.url) and dotted lookups (cfg.get("service.url")) both work.
  • Source order matters: items from later sources override earlier ones.

Features

  • deterministic source ordering; later sources override earlier ones
  • automatic parsing for .yml/.yaml, .json, and .toml
  • ${ENV_VAR:default} interpolation inside string values
  • environment overlays with case-insensitive prefix matching
  • ConfigView offering .get() with dotted paths, .to_dict(), .as_dataclass()
  • stateless loader: creating a new manager or calling reload() rereads from disk

Environment Overrides (Two Ways)

  1. Inline placeholders inside files (resolved at load time):
# in conf/local/service.yml
service:
  webhook: "${SERVICE_WEBHOOK:https://hooks.local/notify}"
  • If SERVICE_WEBHOOK is set in the environment, its value is used.
  • Otherwise, the default https://hooks.local/notify is used.
  1. Environment overlay with a prefix (no file edits needed):
# Let’s suppose you want to raise the timeout via an env var
export APP__SERVICE__TIMEOUT=30
pytest  # or run your app; the manager will pick it up

With EnvSource(prefix="APP", delimiter="__", infer_types=True), keys map as:

  • APP__SERVICE__TIMEOUT=30 -> service.timeout = 30 (int)
  • APP__SERVICE__FEATURES__CACHE=false -> service.features.cache = False (bool)

Loading: One-Call vs Explicit Sources

  • Quick one-call loader for folders only:

    from liteconf import load
    cfg = load(layers=[Path("conf/base"), Path("conf/local")])
    # Recursively loads .yml/.yaml/.json/.toml; later folders override earlier
    
  • Explicit sources for full control (files, env, dict overlays, optional flags):

    from liteconf import ConfigManager, DirectorySource, FileSource, EnvSource, DictOverlay
    manager = ConfigManager([
        DirectorySource(Path("conf/base")),
        DirectorySource(Path("conf/local")),
        FileSource(Path("extra.json")),
        EnvSource(prefix="APP"),
        DictOverlay({"runtime": {"feature_flag": True}}),
    ])
    cfg = manager.load()
    

Use load(...) when you just need layered folders; use ConfigManager when you want to precisely choose and order different kinds of sources.

File Precedence and Duplicate Stems

  • Across sources: later sources override earlier ones.
  • Within a single folder: files are applied in lexicographic filename order.
  • If both messaging.json and messaging.yml exist in the same folder:
    • One-call load(...) (LayeredConfigLoader): both are merged; later filename wins for overlapping keys (usually YAML after JSON).
    • Explicit DirectorySource (ConfigManager): later filename replaces the earlier file’s subtree at that key entirely.

Recommendation: avoid keeping two files with the same stem (e.g., messaging.*) in the same folder. Prefer a single file per logical key per layer to keep intent crystal clear.

Project layout

liteconf/
|-- pyproject.toml
|-- README.md
|-- src/
|   \-- liteconf/
|       |-- __init__.py
|       |-- core.py
|       |-- errors.py
|       |-- exceptions.py
|       |-- loader.py
|       |-- manager.py
|       |-- merger.py
|       |-- namespaces.py
|       \-- sources.py
\-- tests/
    |-- conftest.py
    |-- fixtures/
    |   |-- base/
    |   |   \-- messaging.yml
    |   |-- local/
    |   |   \-- messaging.yml
    |   \-- prod/
    |       |-- messaging.json
    |       \-- messaging.yml
    |-- test_loader.py
    \-- test_manager.py

License

MIT — do anything you want, just keep the notice.

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

liteconf-1.2.4.tar.gz (20.8 kB view details)

Uploaded Source

Built Distribution

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

liteconf-1.2.4-py3-none-any.whl (15.7 kB view details)

Uploaded Python 3

File details

Details for the file liteconf-1.2.4.tar.gz.

File metadata

  • Download URL: liteconf-1.2.4.tar.gz
  • Upload date:
  • Size: 20.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.10

File hashes

Hashes for liteconf-1.2.4.tar.gz
Algorithm Hash digest
SHA256 da4f4c5b5f39ec12b0a5bd2056a2428e6be8b0f556cadc7cbc471cfeac2562e4
MD5 9d4a7046cd5b446b318bba3143a9ae63
BLAKE2b-256 e2de7468727a772599014cb9efc146ed389a796b88dd5b0631ba19c5aca506b0

See more details on using hashes here.

File details

Details for the file liteconf-1.2.4-py3-none-any.whl.

File metadata

  • Download URL: liteconf-1.2.4-py3-none-any.whl
  • Upload date:
  • Size: 15.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.10

File hashes

Hashes for liteconf-1.2.4-py3-none-any.whl
Algorithm Hash digest
SHA256 04edb7b3e76e89337cd1b7057cfdcea1f9ac1f039baf2fe0317a00defd0221e1
MD5 400ccc8cf1a763a29e3c6343fd5a5355
BLAKE2b-256 7b355c3363919e575cc75e3cb7a33b99370c688ad2e505926cb86df2a99c9767

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