Schema-based environment variable management for Python projects
Project description
epicenv
Schema-based environment variable management for Python projects.
Define your environment variables in pyproject.toml with types, defaults, and help text. Use epicenv to create, validate, and manage .env files. Works with any Python project - Django, FastAPI, Flask, or plain Python.
Features
- 📋 Schema-based: Define variables in
pyproject.tomlwith types, defaults, and documentation - ✅ Validation: Catch undefined variables in debug mode before they cause runtime errors
- 🛠️ CLI Tools: Create and compare
.envfiles withuvx epicenv createanduvx epicenv diff - 🎯 Framework Agnostic: Works with Django, FastAPI, Flask, or any Python project
- 🔧 Flexible: Use schema-only, code-only, or hybrid approaches
- 🐍 Type Safe: Full type support (str, bool, int, list, url, json, etc.)
Quick Start
1. Define your schema in pyproject.toml
[tool.epicenv.variables]
SECRET_KEY = {
type = "str",
required = true,
help_text = "Secret key for cryptographic signing",
initial_func = "secrets.token_urlsafe"
}
DEBUG = {
type = "bool",
default = false,
initial = "on",
help_text = "Enable debug mode (never enable in production)"
}
DATABASE_URL = {
type = "dj_db_url",
default = "sqlite:///db.sqlite3",
help_text = "Database connection URL"
}
API_KEY = {
type = "str",
required = true,
help_text = "API key for external service"
}
2. Create a .env file
Use uvx to run without installing:
uvx epicenv create
Or install and use directly:
uv add epicenv
epicenv create
This generates a .env file with help text, types, and initial values:
# This is an initial .env file generated on 2026-01-24...
# Any environment variable with a default can be safely removed or commented out.
# Secret key for cryptographic signing
# type: str
SECRET_KEY=hkXzH0ZFG1YOtKhDsXAqS7rkt50_fZu_bV8YzPXr7VA
# Enable debug mode (never enable in production)
# type: bool
# default: False
DEBUG=on
# Database connection URL
# type: dj_db_url
# default: sqlite:///db.sqlite3
# DATABASE_URL=
# API key for external service
# type: str
API_KEY=
3. Use in your code
from epicenv import Env
env = Env()
env.read_env() # Load from .env file
SECRET_KEY = env.str("SECRET_KEY")
DEBUG = env.bool("DEBUG", default=False)
DATABASE_URL = env.str("DATABASE_URL", default="sqlite:///db.sqlite3")
API_KEY = env.str("API_KEY")
4. Validate your environment
Check for missing required variables:
epicenv validate
Compare your .env file with the schema:
epicenv diff
Installation
For any Python project
uv add epicenv
For Django projects (with dj-database-url, dj-email-url support)
uv add epicenv[django]
Without installation (using uvx)
uvx epicenv create
uvx epicenv diff
uvx epicenv validate
CLI Commands
epicenv create
Create a .env file from your pyproject.toml schema.
epicenv create # Create .env in current directory
epicenv create --path config/.env # Create at specific path
epicenv create --no-backup # Don't backup existing file
epicenv diff
Compare your .env file with the schema and show differences.
epicenv diff # Compare .env with schema
epicenv diff --path config/.env # Compare specific file
Output:
✓ All variables are in sync!
or
Missing required variables:
• API_KEY - API key for external service
Missing optional variables (have defaults):
• DATABASE_URL (default: sqlite:///db.sqlite3) - Database connection URL
Variables in .env file not defined in schema:
• OLD_VARIABLE
epicenv validate
Validate current environment against schema.
epicenv validate # Validate environment
epicenv validate --strict # Exit with error code if validation fails
Validation Mode
Validation is automatically enabled when DEBUG=true. Control validation behavior with the EPICENV_VALIDATE environment variable:
auto(default): Validate whenDEBUG=true, otherwise no validationstrict: Always validate and raise errors for undefined variableswarn: Always validate but only warn (don't raise errors)off: Disable validation completely
# Auto mode (default) - validates only when DEBUG=true
DEBUG=true python manage.py runserver # Validates!
DEBUG=false python manage.py runserver # No validation
# Force strict validation regardless of DEBUG
EPICENV_VALIDATE=strict python manage.py runserver
# Warn about undefined variables but don't fail
EPICENV_VALIDATE=warn python manage.py runserver
# Disable validation completely
EPICENV_VALIDATE=off python manage.py runserver
In your code:
from epicenv import Env
env = Env() # Validation mode controlled by EPICENV_VALIDATE env var
env.read_env()
# If MY_VAR is not in pyproject.toml schema and validation is enabled:
value = env.str("MY_VAR") # UndefinedVariableError!
Error message:
UndefinedVariableError: Environment variable 'MY_VAR' is not defined in pyproject.toml schema.
Add it to [tool.epicenv.variables] in your pyproject.toml:
[tool.epicenv.variables]
MY_VAR = { type = "str", help_text = "Description here" }
Or disable validation by setting EPICENV_VALIDATE=off
Schema Reference
Field Types
All environs types are supported:
- Basic:
str,bool,int,float,decimal - Collections:
list,dict,json - Date/Time:
date,datetime,time,timedelta - Specialized:
url,uuid,path,enum,log_level - Django (with
[django]extra):dj_db_url,dj_email_url,dj_cache_url
Schema Fields
[tool.epicenv.variables]
MY_VARIABLE = {
type = "str", # Variable type (required)
required = true, # Is this variable required? (default: true if no default)
default = "value", # Default value if not set
help_text = "Description", # Documentation for this variable
initial = "initial_value", # Static initial value for .env generation
initial_func = "module.function" # Callable for dynamic initial values
}
Initial Value Functions
Use initial_func to generate dynamic values:
[tool.epicenv.variables]
SECRET_KEY = {
type = "str",
initial_func = "secrets.token_urlsafe" # Python stdlib
}
DJANGO_SECRET = {
type = "str",
initial_func = "django.core.management.utils.get_random_secret_key"
}
CUSTOM_VALUE = {
type = "str",
initial_func = "myapp.utils.generate_api_key" # Your own function
}
Django Integration
Option 1: Schema-based (Recommended)
Define variables in pyproject.toml and use the Env class:
# settings.py
from pathlib import Path
from epicenv import Env
BASE_DIR = Path(__file__).resolve().parent.parent
env = Env() # Validation automatically enabled when DEBUG=true
env.read_env(BASE_DIR / ".env")
SECRET_KEY = env.str("SECRET_KEY")
DEBUG = env.bool("DEBUG", default=False)
DATABASES = {"default": env.dj_db_url("DATABASE_URL", default="sqlite:///db.sqlite3")}
Then use CLI commands:
epicenv create # Instead of ./manage.py create_env_file
epicenv diff # Instead of ./manage.py diff_env_file
Option 2: Django Management Commands (Legacy)
For backward compatibility, Django management commands still work:
# settings.py
INSTALLED_APPS = [
...
"epicenv", # Add to INSTALLED_APPS
]
from epicenv import Env
env = Env()
env.read_env(BASE_DIR / ".env")
# Use help_text, initial, initial_func as before
SECRET_KEY = env.str(
"SECRET_KEY",
initial_func="django.core.management.utils.get_random_secret_key",
help_text="Django's secret key",
)
Then use Django commands:
./manage.py create_env_file
./manage.py diff_env_file
Examples
FastAPI Project
# pyproject.toml
[tool.epicenv.variables]
APP_NAME = { type = "str", default = "My API", help_text = "Application name" }
API_HOST = { type = "str", default = "0.0.0.0", help_text = "API host" }
API_PORT = { type = "int", default = 8000, help_text = "API port" }
DATABASE_URL = { type = "url", required = true, help_text = "Database URL" }
REDIS_URL = { type = "url", default = "redis://localhost:6379", help_text = "Redis URL" }
LOG_LEVEL = { type = "log_level", default = "INFO", help_text = "Logging level" }
# config.py
from epicenv import Env
env = Env()
env.read_env()
APP_NAME = env.str("APP_NAME", default="My API")
API_HOST = env.str("API_HOST", default="0.0.0.0")
API_PORT = env.int("API_PORT", default=8000)
DATABASE_URL = env.url("DATABASE_URL")
REDIS_URL = env.url("REDIS_URL", default="redis://localhost:6379")
LOG_LEVEL = env.log_level("LOG_LEVEL", default="INFO")
Flask Project
# pyproject.toml
[tool.epicenv.variables]
FLASK_APP = { type = "str", default = "app.py", help_text = "Flask app entry point" }
FLASK_ENV = { type = "str", default = "production", help_text = "Flask environment" }
SECRET_KEY = { type = "str", required = true, help_text = "Flask secret key", initial_func = "secrets.token_hex" }
DATABASE_URL = { type = "url", required = true, help_text = "Database URL" }
# config.py
from epicenv import Env
env = Env()
env.read_env()
class Config:
FLASK_APP = env.str("FLASK_APP", default="app.py")
FLASK_ENV = env.str("FLASK_ENV", default="production")
SECRET_KEY = env.str("SECRET_KEY")
SQLALCHEMY_DATABASE_URI = env.url("DATABASE_URL")
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
License
This project is licensed under the MIT License.
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
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 epicenv-1.0.0.tar.gz.
File metadata
- Download URL: epicenv-1.0.0.tar.gz
- Upload date:
- Size: 39.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2cf4c89ae3731629ee714f042301b67f5ebd8a2a8a9ff0b2cff2719c4e4b3d94
|
|
| MD5 |
d60939d9ed5dee6adb606bd5cf22a75e
|
|
| BLAKE2b-256 |
0adedd2856e1b1e0d55adf14022e824fe1a628cc2acb5c38944221224a6711a8
|
File details
Details for the file epicenv-1.0.0-py3-none-any.whl.
File metadata
- Download URL: epicenv-1.0.0-py3-none-any.whl
- Upload date:
- Size: 16.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3ce256bc30bf046e6cfcab37b7a0b6d84ca9ce7037c1082c03004999fa6161b
|
|
| MD5 |
ffc6996f15e33b99c2d1120f20911147
|
|
| BLAKE2b-256 |
03095ab47c3cadefdba5109c19e8af0ce8d77e3ad387fd82395f381d3acf1abe
|