Policies
Failure Policies¶
Control how the loader handles errors from different configuration sources. Failure policies allow you to specify explicit behavior for each provider, ensuring predictable behavior in production while maintaining resilience in development.
Why Explicit Failure Policies?¶
Problem¶
When a cloud provider fails (network outage, authentication error, rate limit), what should happen?
- Fail fast? - Application crashes, but you know immediately
- Silently continue? - Application runs, but might be misconfigured
- Log and continue? - Application runs, but with warnings
Different environments need different behaviors. Production should fail fast. Development should be resilient.
Decision¶
Explicit per-provider failure policies with three options:
fail- Raise error immediately (production)warn- Log warning and continue (development)fallback- Silently continue (resilient)
Policy Options¶
Fail (Production)¶
Behavior: Raises ProviderError immediately when provider fails.
config = load_env(
providers=[azure_provider],
failure_policy={"azure": "fail"}
)
# If Azure Key Vault is unavailable:
# Raises: ProviderError: Provider azure error: Connection timeout
When to use: - Production environments - Critical providers (secrets must come from secure source) - When misconfiguration is worse than failure
Example scenario:
# Production: secrets must come from Azure
config = load_env(
env="prod",
providers=[azure_provider],
failure_policy={"azure": "fail"} # Fail if Azure unavailable
)
# Application won't start with wrong secrets
Warn (Development)¶
Behavior: Logs warning message and continues loading from other sources.
config = load_env(
providers=[azure_provider],
failure_policy={"azure": "warn"}
)
# If Azure Key Vault is unavailable:
# WARNING: Provider azure error: Connection timeout
# Continues loading from .env files
When to use: - Development environments - Optional providers (nice to have, not required) - When you want visibility but not failure
Example scenario:
# Development: Azure optional, use .env if unavailable
config = load_env(
env="dev",
providers=[azure_provider],
failure_policy={"azure": "warn"} # Warn but continue
)
# Application runs with .env values, but logs warning
Fallback (Resilient)¶
Behavior: Silently continues, no error or warning.
config = load_env(
providers=[azure_provider],
failure_policy={"azure": "fallback"}
)
# If Azure Key Vault is unavailable:
# Silently continues, uses .env files
# No error, no warning
When to use: - Resilient systems (must work even if providers unavailable) - Optional providers (completely optional) - When failures are expected and handled elsewhere
Example scenario:
# Resilient: must work even if cloud unavailable
config = load_env(
providers=[azure_provider, aws_provider],
failure_policy={
"azure": "fallback", # Silently continue
"aws": "fallback" # Silently continue
}
)
# Application always starts, uses .env if cloud unavailable
Multiple Providers¶
You can set different policies for different providers:
config = load_env(
providers=[azure_provider, aws_provider, filesystem_provider],
failure_policy={
"azure": "fail", # Critical in production
"aws": "fallback", # Optional
"filesystem": "warn" # Development only
}
)
Priority: Later providers override earlier ones if they have the same keys.
Default Policies¶
If no policy is specified, defaults are used:
# Default policies
{
"azure": "fail", # Production: fail on error
"aws": "fallback", # Fallback if unavailable
"filesystem": "warn", # Warn on filesystem errors
"docker": "warn", # Warn on Docker secret errors
"kubernetes": "warn" # Warn on K8s errors
}
You can override defaults:
# Override defaults
config = load_env(
providers=[azure_provider],
failure_policy={"azure": "warn"} # Override default "fail"
)
Policy Resolution¶
Policies are matched by provider name:
- Exact match:
"azure"matchesAzureKeyVaultProvider - Partial match:
"azure"matches"AzureKeyVaultProvider"(case-insensitive) - Default: If no match, uses default policy
# These all match AzureKeyVaultProvider:
failure_policy = {
"azure": "fail",
"AzureKeyVaultProvider": "fail",
"Azure": "fail"
}
Real-World Scenarios¶
Scenario 1: Production Deployment¶
Requirement: Secrets must come from Azure Key Vault. Application should not start if Azure is unavailable.
config = load_env(
env="prod",
providers=[azure_provider],
failure_policy={"azure": "fail"} # Fail fast
)
# Application won't start with wrong secrets
Scenario 2: Development Environment¶
Requirement: Use Azure if available, but fall back to .env file for local development.
config = load_env(
env="dev",
providers=[azure_provider],
failure_policy={"azure": "warn"} # Warn but continue
)
# Application runs with .env, logs warning about Azure
Scenario 3: Resilient System¶
Requirement: Application must start even if all cloud providers are unavailable.
config = load_env(
providers=[azure_provider, aws_provider],
failure_policy={
"azure": "fallback", # Silently continue
"aws": "fallback" # Silently continue
}
)
# Application always starts, uses .env if cloud unavailable
Scenario 4: Multi-Provider Setup¶
Requirement: Azure is critical, AWS is optional, filesystem is for development.
config = load_env(
providers=[azure_provider, aws_provider, filesystem_provider],
failure_policy={
"azure": "fail", # Critical: fail if unavailable
"aws": "fallback", # Optional: silently continue
"filesystem": "warn" # Development: warn but continue
}
)
Error Handling¶
Fail Policy¶
try:
config = load_env(
providers=[azure_provider],
failure_policy={"azure": "fail"}
)
except ProviderError as e:
logger.error(f"Provider error: {e}")
# Handle error (alert, retry, etc.)
sys.exit(1)
Warn Policy¶
# Warning logged, but continues
config = load_env(
providers=[azure_provider],
failure_policy={"azure": "warn"}
)
# No exception raised
# Check logs for warnings
Fallback Policy¶
# Silently continues
config = load_env(
providers=[azure_provider],
failure_policy={"azure": "fallback"}
)
# No exception, no warning
# Application continues with fallback values
Best Practices¶
1. Use "fail" in Production¶
# Production: fail fast on critical providers
config = load_env(
env="prod",
providers=[azure_provider],
failure_policy={"azure": "fail"}
)
2. Use "warn" in Development¶
# Development: warn but continue
config = load_env(
env="dev",
providers=[azure_provider],
failure_policy={"azure": "warn"}
)
3. Use "fallback" for Resilience¶
# Resilient: must work even if providers unavailable
config = load_env(
providers=[optional_provider],
failure_policy={"optional_provider": "fallback"}
)
4. Document Policies¶
# Document why each policy is set
failure_policy = {
"azure": "fail", # Critical: secrets must come from Azure
"aws": "fallback", # Optional: can use .env if AWS unavailable
"filesystem": "warn" # Development: warn about filesystem usage
}
5. Environment-Specific Policies¶
import os
env = os.getenv("ENVIRONMENT", "dev")
policies = {
"prod": {"azure": "fail", "aws": "fail"},
"dev": {"azure": "warn", "aws": "fallback"},
"test": {"azure": "fallback", "aws": "fallback"}
}
config = load_env(
env=env,
providers=[azure_provider, aws_provider],
failure_policy=policies.get(env, {})
)
Common Mistakes¶
Mistake 1: Using "fallback" in Production¶
# ❌ WRONG: Silent failures in production
config = load_env(
env="prod",
providers=[azure_provider],
failure_policy={"azure": "fallback"} # Bad!
)
# Application might start with wrong secrets
# ✅ CORRECT: Fail fast in production
config = load_env(
env="prod",
providers=[azure_provider],
failure_policy={"azure": "fail"} # Good!
)
Mistake 2: Using "fail" in Development¶
# ❌ WRONG: Fails in development when Azure unavailable
config = load_env(
env="dev",
providers=[azure_provider],
failure_policy={"azure": "fail"} # Bad for dev!
)
# Can't develop locally without Azure access
# ✅ CORRECT: Warn in development
config = load_env(
env="dev",
providers=[azure_provider],
failure_policy={"azure": "warn"} # Good for dev!
)
Mistake 3: Not Setting Policies¶
# ❌ WRONG: Uses defaults, might not be what you want
config = load_env(providers=[azure_provider])
# ✅ CORRECT: Explicit policies
config = load_env(
providers=[azure_provider],
failure_policy={"azure": "fail"} # Explicit
)