SprigConfig

Spring Boot-style configuration for Python applications

View the Project on GitHub derikgw/sprig-config

sprig_config_logo_horizontal_150_35.svg

SprigConfig

Spring Boot-style configuration management for Python.

SprigConfig is a production-ready, lightweight configuration system that brings layered YAML configuration, runtime-driven profile selection, deep merge semantics, secure secrets handling, and complete provenance tracking to Python applications.

Think of it as Spring Boot’s configuration philosophy for Python—but designed from the ground up for Python’s simplicity and flexibility.


What Is SprigConfig?

SprigConfig solves a real problem: configuration management should be predictable, debuggable, and secure—especially at 3am during an outage.

Core Philosophy

Layered configuration with deterministic deep merge semantics ✅ Runtime-driven profiles (dev, test, prod) that never come from files ✅ Recursive imports with cycle detection and provenance tracking ✅ Encrypted secrets that stay encrypted until you need them ✅ Deterministic, debuggable behavior you can reason about ✅ Backward compatibility is critical ✅ Secure by default for sensitive values


Quick Start

Installation

pip install sprig-config

Or with Poetry:

poetry add sprig-config

Requirements: Python 3.13+

5-Minute Example

Configuration files (config/ directory):

# config/application.yml (base config)
server:
  port: 8080
  host: localhost
  debug: false

database:
  host: localhost
  port: 5432

logging:
  level: INFO
# config/application-dev.yml (development overlay)
server:
  port: 9090      # Override base
  debug: true

logging:
  level: DEBUG    # Override base

Python code:

from sprigconfig import load_config

# Load dev configuration (profile is runtime-driven)
cfg = load_config(profile="dev")

print(cfg["server.port"])      # → 9090 (from dev overlay)
print(cfg["server.host"])      # → localhost (from base)
print(cfg["server.debug"])     # → true (from dev overlay)
print(cfg["database.host"])    # → localhost (from base, not overridden)
print(cfg["app.profile"])      # → dev (injected at runtime)

That’s it! Configuration is loaded, merged, and available via simple dotted-key access.


Key Features

Feature What It Does
Profile Overlays Environment-specific config (dev, test, prod) selected at runtime
Deep Merge Predictable layering with collision warnings and full provenance
Recursive Imports Modular configuration with imports: directive and cycle detection
Secure Secrets Fernet-encrypted ENC(...) values, decrypted on-demand with lazy evaluation
Format Support YAML (recommended), JSON, and TOML
CLI Tools Inspect, debug, and validate merged configuration from command line
Dependency Injection ConfigValue descriptors, @ConfigurationProperties, and @config_inject decorators
Dynamic Instantiation Hydra-style _target_ support for instantiating classes from config
Provenance Tracking Know exactly where every value came from

Supported Formats

# YAML (default, recommended)
server:
  port: 8080
{
  "server": {
    "port": 8080
  }
}
[server]
port = 8080

All files in a single load must use the same format.


Documentation Roadmap

🚀 I’m New to SprigConfig

Start here:

  1. Philosophy — Design principles and why SprigConfig exists
  2. Getting Started — Installation and first steps
  3. Configuration Guide — Core concepts, API, dotted-key access
  4. Quick Example — See it working (you’re reading it!)

📚 Understanding How It Works

Dive deeper into SprigConfig’s behavior:

🔧 Advanced Usage

For power users and framework integration:

👨‍💻 Contributing & Development

For developers working on SprigConfig itself:

🔐 Operational

Guides for running SprigConfig in production:


Architecture Overview

Configuration Loading Flow

SprigConfig follows a predictable, debuggable flow:

1. Profile Resolution (runtime-driven)
   ↓
   Explicit: load_config(profile="prod")
   Or: APP_PROFILE=prod environment variable
   Or: pytest context → "test"
   Or: default → "dev"

2. File Loading & Merging
   ↓
   Load application.yml (base)
   + Overlay application-<profile>.yml
   + Process recursive imports: [...]
   = Deep merge with collision warnings

3. Value Processing
   ↓
   Expand ${VAR} environment variables
   Convert ENC(...) → LazySecret objects
   Inject app.profile (runtime value)
   Inject sprigconfig._meta (provenance)

4. Return Config Object
   ↓
   Dict-like interface with dotted-key access

Core Modules

Located in the SprigConfig source (src/sprigconfig/):

Module Purpose
config_loader.py Main loading logic, merging, profile selection
config.py Config class (dict-like interface, dotted-key access)
lazy_secret.py LazySecret class for deferred, secure decryption
deepmerge.py Deep merge algorithm with collision warnings
injection.py Dependency injection (ConfigValue, @ConfigurationProperties, @config_inject)
instantiate.py Dynamic class instantiation via _target_ patterns
exceptions.py Custom exceptions (ConfigLoadError, etc.)
config_singleton.py Thread-safe cached loader
cli.py Command-line interface for inspection/debugging

Design Principles

SprigConfig is built on these core principles (non-negotiable):

  1. Layered YAML with deep merge semantics — Not just overrides, but intelligent merging
  2. Runtime-driven profile selection — Profiles come from code/environment, never from files
  3. Deterministic, debuggable — You can always explain where a value came from
  4. Backward compatibility — Core behavior changes carefully, with deprecation
  5. Provenance tracking — Full metadata about every value’s origin
  6. Secure by default — Secrets are encrypted; decryption is opt-in and explicit

Use Cases

SprigConfig is ideal for:


Comparison

How does SprigConfig compare?

Feature SprigConfig Spring Python python-dotenv Pydantic hydra pyyaml
Layered config
Profile overlays
Recursive imports
Encrypted secrets
Provenance tracking
Multiple formats YAML only
CLI debugging
Spring Boot style ⚠️
Dependency injection
Dynamic instantiation
Validation ⚠️ (manual)
Python 3.13+

Notes on Spring Python: Spring Python (v1.2.1) is an older project that provides IoC container and YAML/XML configuration, inspired by Spring Framework. However, it only supports Python 2.6+ (not Python 3) and lacks modern features like profile overlays, encrypted secrets, and provenance tracking. For Python developers seeking Spring-style patterns, SprigConfig offers a modern, actively-maintained alternative.



License

MIT License. See LICENSE for details.


Getting Help


SprigConfig — Stable. Secure. Predictable.