Enhanced environment configuration with better errors and multi-source loading
Project description
envcraft
Stop debugging missing env vars. Type-safe configuration with fuzzy error suggestions, auto-generated
.env.example, and multi-file cascading — in one class.
The Problem
$ python app.py
pydantic_core._pydantic_core.ValidationError: 1 validation error for Settings
DATABSE_URL
Field required [type=missing, input_value={}, input_type=dict]
You stare at the error. Was it DATABASE_URL? DB_URL? Which .env file is active? Did .env.local override it? Is the value even the right type?
With envcraft:
❌ Environment Configuration Error:
• database_url: Field required
→ Set DATABASE_URL in your .env file or environment
💡 Did you mean: DATABASE_URL?
Plus: your .env.example is auto-generated, your secrets never leak into logs, and environment-specific overrides just work.
Install
pip install envcraft
Quick Start
from envcraft import EnvCraft
from pydantic import Field
class Config(EnvCraft):
database_url: str = Field(..., description="PostgreSQL connection string")
api_key: str = Field(..., description="External API key")
debug: bool = False
max_workers: int = 4
config = Config.load()
# ✅ Type-validated, cached, thread-safe, with helpful errors if anything's wrong
Create a .env:
DATABASE_URL=postgresql://localhost/mydb
API_KEY=sk-secret-key
DEBUG=true
MAX_WORKERS=8
That's it. EnvCraft handles type coercion, multi-file loading, variable interpolation, and auto-generates .env.example from your schema.
Why envcraft?
| Feature | python-dotenv | pydantic-settings | envcraft |
|---|---|---|---|
| Type validation | ❌ | ✅ | ✅ |
| Fuzzy error suggestions | ❌ | ❌ | ✅ |
Auto .env.example generation |
❌ | ❌ | ✅ |
| Multi-file cascade | ❌ | ⚠️ | ✅ |
Variable interpolation (${VAR}) |
❌ | ❌ | ✅ |
| Secret masking in logs | ❌ | ❌ | ✅ |
| Hot reload with callbacks | ❌ | ❌ | ✅ |
| CLI tools | ❌ | ❌ | ✅ |
| Secret backends (AWS/Azure/Vault) | ❌ | ⚠️ | ✅ |
| Strict mode (catch drift) | ❌ | ❌ | ✅ |
| Nested config groups | ❌ | ✅ | ✅ |
Features
Multi-File Cascade
Load order: .env → .env.{environment} → .env.local
config = Config.load(env="production")
# Loads: .env → .env.production → .env.local (each overrides the previous)
Variable Interpolation
# .env
BASE_URL=https://api.example.com
AUTH_URL=${BASE_URL}/auth
CALLBACK_URL=${BASE_URL}/callback
Secret Masking
from envcraft import EnvCraft, Secret
class Config(EnvCraft):
api_key: Secret[str]
database_url: str
config = Config.load()
print(config.api_key) # Secret('***')
print(config.api_key.get()) # actual value (use intentionally)
Secrets never accidentally appear in logs, repr, or serialized output.
Secret Backends
from envcraft import Secret
class Config(EnvCraft):
# Load from AWS Secrets Manager
db_password: Secret[str] = Secret.from_aws("prod/db-password", region="us-east-1")
# Load from Azure Key Vault
api_key: Secret[str] = Secret.from_azure("api-key", vault_url="https://myvault.vault.azure.net")
# Load from HashiCorp Vault
token: Secret[str] = Secret.from_vault("app/token", url="https://vault.example.com")
Install backend extras:
pip install envcraft[aws] # AWS Secrets Manager
pip install envcraft[azure] # Azure Key Vault
pip install envcraft[vault] # HashiCorp Vault
pip install envcraft[all] # All backends
Thread-Safe Caching & Hot Reload
# Singleton — same instance across your app
config = Config.load() # loads once, cached thereafter
# Hot reload when env files change
config = Config.reload()
# Register callbacks on reload
@Config.on_reload
def handle_reload(new_config):
print(f"Config reloaded! Debug is now: {new_config.debug}")
Strict Mode
Catch config drift — fail if .env contains variables not in your schema:
config = Config.load(strict=True)
# Raises ValueError if .env has unknown variables
Nested Configs
from pydantic import BaseModel
class DatabaseConfig(BaseModel):
host: str = "localhost"
port: int = 5432
name: str = "mydb"
class Config(EnvCraft):
database: DatabaseConfig = DatabaseConfig()
debug: bool = False
# .env
DATABASE__HOST=prod-db.example.com
DATABASE__PORT=5432
DATABASE__NAME=production
Source Tracing
See which file provided each variable:
config = Config.load(show_sources=True)
📋 Environment Variable Sources:
DATABASE_URL = postgresql://localhost/mydb
└─ loaded from .env.local
API_KEY = ***
└─ loaded from .env
DEBUG = True
└─ loaded from .env.production
CLI
envcraft check # Validate all config vars — shows ✓/✗ per variable
envcraft generate # Auto-generate .env.example from your schema
envcraft docs # Generate CONFIG.md documentation
envcraft explain DATABASE_URL # Explain a specific variable
envcraft check output:
🔍 Configuration Diagnosis:
✓ DATABASE_URL present
✓ API_KEY present
✓ DEBUG using default (False)
✗ REDIS_URL missing (required)
❌ Some required variables are missing
API Reference
Config.load()
Config.load(
env: str = None, # Environment name (loads .env.{env})
auto_generate_example: bool = True, # Auto-create .env.example
show_sources: bool = False, # Print which file supplied each var
strict: bool = False, # Fail on unknown variables
cache: bool = True, # Cache instance (thread-safe singleton)
)
Config.reload()
Force-reload from disk. Triggers registered callbacks.
Config.diagnose()
Print status of all variables without loading (useful for CI).
Config.generate_example(output=".env.example")
Generate .env.example from schema with types and descriptions.
Config.generate_docs(output="CONFIG.md")
Generate Markdown documentation of all config variables.
Framework Integration
FastAPI
from fastapi import FastAPI, Depends
from envcraft import EnvCraft
from pydantic import Field
class Settings(EnvCraft):
database_url: str = Field(..., description="Database connection")
secret_key: str = Field(..., description="JWT signing key")
debug: bool = False
def get_settings() -> Settings:
return Settings.load()
app = FastAPI()
@app.get("/health")
def health(settings: Settings = Depends(get_settings)):
return {"debug": settings.debug}
Django
# settings.py
from envcraft import EnvCraft
class DjangoEnv(EnvCraft):
secret_key: str
database_url: str
debug: bool = False
allowed_hosts: str = "localhost"
env = DjangoEnv.load()
SECRET_KEY = env.secret_key
DEBUG = env.debug
Requirements
- Python 3.8+
- pydantic >= 2.0.0
- pydantic-settings >= 2.0.0
Development
git clone https://github.com/ArnabbLank/EnvCraft.git
cd EnvCraft
pip install -e ".[dev]"
pytest tests/ -v
Contributing
Contributions welcome:
- 🐛 Report bugs
- ✨ Suggest features
- 🧪 Add tests
- 📖 Improve docs
License
MIT © 2026 Arnab Sen
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 envcraft-0.1.0.tar.gz.
File metadata
- Download URL: envcraft-0.1.0.tar.gz
- Upload date:
- Size: 17.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9be58f3fc7594f3e71a0904c67a33304f6b9e106beeda1e96e1fc4a4e9f36a8
|
|
| MD5 |
bb772f738853cc80d8141c2e5915f7d4
|
|
| BLAKE2b-256 |
0b280ecaa4ed183b0311bef8010d4d069bd791334131c8fa329f2f3e02b2847a
|
File details
Details for the file envcraft-0.1.0-py3-none-any.whl.
File metadata
- Download URL: envcraft-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2485daf7c96153fd0619850db597b0858b62da24d38eb8cee3d2b5f69b816fae
|
|
| MD5 |
cd81a787fd220cdc80ca827ac35b0337
|
|
| BLAKE2b-256 |
bca7e4194e911d7c249d6bcc0dfa4057646c95b4ba480d825f209e550b8e77cb
|