Type-safe environment variables with automatic validation and documentation generation
Project description
EnvSchema
Type-safe environment variables with automatic validation and documentation generation
Documentation | Installation | Quick Start | Examples | Читать на русском
🎯 Overview
EnvSchema is a lightweight Python library for managing environment variables with automatic type validation, nested schemas support, and built-in documentation generation. It eliminates boilerplate code and provides type safety for your application configuration.
✨ Key Features
- 🔒 Type-safe: Full typing support with automatic validation
- 🎨 Clean syntax: Pythonic API using dataclass-like definitions
- 🏗️ Nested schemas: Support for complex hierarchical configurations
- 📝 Auto-documentation: Generate
.env.exampleand Markdown docs automatically - 🔄 Flexible types: Support for
str,int,float,bool,list,dict,Optional - 🌐
.envsupport: Load variables from.envfiles (requirespython-dotenv) - ⚡ Zero dependencies: Core functionality works without external packages
- 🎯 Clear errors: Detailed validation error messages
📦 Installation
# Basic installation
pip install envschema
# With .env file support
pip install envschema[dotenv]
# or
pip install envschema python-dotenv
🚀 Quick Start
Basic Example
from envschema import EnvSchema, Field
class Settings(EnvSchema):
# Required fields
api_key: str
database_url: str
# Optional fields with defaults
debug: bool = False
port: int = 8000
# Field with custom env name and description
secret: str = Field(
env="SECRET_KEY",
description="Secret key for encryption"
)
# Optional field
redis_url: str | None = None
# Load from environment variables
settings = Settings.load()
# Access values
print(settings.api_key)
print(settings.port)
With .env File
# Load from .env file
settings = Settings.load(dotenv_path=".env")
# Auto-discover .env file in current or parent directories
settings = Settings.load(dotenv_path=True)
# Override system env with .env values
settings = Settings.load(dotenv_path=".env", dotenv_override=True)
📚 Examples
Supported Types
from envschema import EnvSchema
class Config(EnvSchema):
# String
name: str = "default"
# Integer
workers: int = 4
# Float
timeout: float = 30.5
# Boolean (accepts: true/false, yes/no, 1/0, on/off)
enabled: bool = True
# List (comma-separated or JSON array)
hosts: list[str] = ["localhost"]
allowed_ids: list[int] = [1, 2, 3]
# Dict (JSON object)
metadata: dict = {"version": "1.0"}
# Optional
optional_value: str | None = None
Environment variables:
NAME=myapp
WORKERS=8
TIMEOUT=60.0
ENABLED=true
HOSTS=host1,host2,host3
ALLOWED_IDS=[10, 20, 30]
METADATA={"key": "value"}
Nested Schemas
from envschema import EnvSchema, Field
class DatabaseConfig(EnvSchema):
host: str = "localhost"
port: int = 5432
name: str
user: str
password: str
class RedisConfig(EnvSchema):
host: str = "localhost"
port: int = 6379
class Settings(EnvSchema):
# Nested schemas with auto prefix
database: DatabaseConfig
redis: RedisConfig
# Nested schema with custom prefix
cache: RedisConfig = Field(prefix="CACHE_")
# Load configuration
settings = Settings.load()
# Access nested values
print(settings.database.host)
print(settings.redis.port)
Environment variables:
# Database config (prefix: DATABASE_)
DATABASE_HOST=db.example.com
DATABASE_PORT=5432
DATABASE_NAME=mydb
DATABASE_USER=admin
DATABASE_PASSWORD=secret123
# Redis config (prefix: REDIS_)
REDIS_HOST=redis.example.com
REDIS_PORT=6379
# Cache config (custom prefix: CACHE_)
CACHE_HOST=cache.example.com
CACHE_PORT=6380
Custom Field Configuration
from envschema import EnvSchema, Field
class Settings(EnvSchema):
# Custom environment variable name
api_key: str = Field(env="SECRET_API_KEY")
# With description for documentation
max_retries: int = Field(
default=3,
description="Maximum number of retry attempts"
)
# Custom prefix for nested schema
db: DatabaseConfig = Field(
prefix="DB_",
description="Database configuration"
)
Validation and Error Handling
from envschema import EnvSchema, EnvSchemaError
class Settings(EnvSchema):
port: int
debug: bool
try:
settings = Settings.load()
except EnvSchemaError as e:
# Get detailed error information
print(e)
# Access individual errors
for error in e.errors:
print(f"Field: {error.field_name}")
print(f"Env var: {error.env_var}")
print(f"Message: {error.message}")
Example error output:
Failed to load environment variables (2 errors):
* PORT: missing required environment variable (expected type: int)
* DEBUG: invalid boolean value 'maybe' (expected: true/false/yes/no/1/0/on/off) (expected type: bool) [got: 'maybe']
📖 Documentation Generation
Generate .env.example
from envschema.docs import DocumentationGenerator
class Settings(EnvSchema):
api_key: str = Field(description="API key for external service")
debug: bool = False
port: int = Field(default=8000, description="Server port")
# Generate .env.example file
generator = DocumentationGenerator(Settings)
generator.generate_example_env(".env.example")
Generated .env.example:
# API key for external service required
API_KEY=your_value_here
# default: false
DEBUG=false
# Server port default: 8000
PORT=8000
Generate Markdown Documentation
# Generate markdown documentation
docs = generator.generate_markdown_docs()
print(docs)
# Or save to file
with open("ENV_VARS.md", "w") as f:
f.write(docs)
Generated documentation includes:
- Overview table with all variables
- Types and requirements
- Default values
- Descriptions
- Usage examples
🔧 Advanced Features
Custom Type Casters
from datetime import timedelta
from envschema.casters import register_caster
def cast_timedelta(value: str) -> timedelta:
"""Cast string to timedelta (expects seconds)."""
return timedelta(seconds=int(value))
# Register custom caster
register_caster(timedelta, cast_timedelta)
class Settings(EnvSchema):
timeout: timedelta = timedelta(seconds=30)
Working with Prefixes
class Settings(EnvSchema):
api_key: str
# Load with global prefix
settings = Settings.load(prefix="MYAPP_")
# Expects: MYAPP_API_KEY
Load from Custom Dict
# Load from custom dictionary (useful for testing)
custom_env = {
"API_KEY": "test-key",
"DEBUG": "true"
}
settings = Settings.load(env=custom_env)
🧪 Testing
import pytest
from envschema import EnvSchema, EnvSchemaError
def test_settings_validation():
"""Test settings validation."""
test_env = {
"API_KEY": "test-key",
"PORT": "8000"
}
settings = Settings.load(env=test_env)
assert settings.api_key == "test-key"
assert settings.port == 8000
def test_missing_required_field():
"""Test missing required field raises error."""
with pytest.raises(EnvSchemaError) as exc_info:
Settings.load(env={})
assert "missing required environment variable" in str(exc_info.value)
📋 Best Practices
- Use type hints: Always specify types for better validation
- Add descriptions: Help your team understand configuration options
- Group related settings: Use nested schemas for logical grouping
- Generate documentation: Keep
.env.exampleand docs up-to-date - Validate early: Load settings at application startup
- Use
.envfor development: Keep production secrets secure
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
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
File details
Details for the file envschema-1.0.0.tar.gz.
File metadata
- Download URL: envschema-1.0.0.tar.gz
- Upload date:
- Size: 34.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b91aa9db5f8367e79035418d36fea0489ae566a5c251ab9ec17b3ee4cf6864e
|
|
| MD5 |
d933131e1d55b6961e410513f1d42c42
|
|
| BLAKE2b-256 |
d4dcf52f0d8996ad1f5c6b11e73adf6ee8e5d81888432a7eaf8402a3a75e92d5
|