Basic Usage¶
Variable Expansion¶
envresolve supports variable expansion using ${VAR}
and $VAR
syntax.
Simple Variable Expansion¶
Multiple Variables¶
You can reference multiple variables in a single string:
from envresolve import expand_variables
env = {
"VAULT_NAME": "my-vault",
"SECRET_NAME": "db-password"
}
result = expand_variables("akv://${VAULT_NAME}/${SECRET_NAME}", env)
print(result) # Output: akv://my-vault/db-password
Nested Variable Expansion¶
Variables can reference other variables:
from envresolve import expand_variables
env = {
"ENVIRONMENT": "prod",
"VAULT_NAME": "${ENVIRONMENT}-vault",
"SECRET_URI": "akv://${VAULT_NAME}/api-key"
}
result = expand_variables(env["SECRET_URI"], env)
print(result) # Output: akv://prod-vault/api-key
Using Environment Variables¶
With os.environ¶
Use EnvExpander
to expand variables from the current environment:
import os
from envresolve import EnvExpander
# Set environment variable
os.environ["VAULT_NAME"] = "production-vault"
expander = EnvExpander()
result = expander.expand("akv://${VAULT_NAME}/secret")
print(result) # Output: akv://production-vault/secret
Snapshot Behavior
EnvExpander
takes a snapshot of os.environ
at initialization time. Changes to environment variables after initialization won't be reflected.
With .env Files¶
Use DotEnvExpander
to expand variables from a .env
file:
from envresolve import DotEnvExpander
# Contents of .env:
# VAULT_NAME=my-company-vault
# DB_PASSWORD=akv://${VAULT_NAME}/db-password
# API_KEY=akv://${VAULT_NAME}/api-key
expander = DotEnvExpander(".env")
db_password_uri = expander.expand("${DB_PASSWORD}")
api_key_uri = expander.expand("${API_KEY}")
print(db_password_uri) # Output: akv://my-company-vault/db-password
print(api_key_uri) # Output: akv://my-company-vault/api-key
Secret Resolution¶
envresolve can resolve secrets referenced with akv://
URIs. Provider
registration is explicit so you only pay the dependency cost when you opt in.
1. Install the Azure extra (once per environment)¶
2. Register the provider¶
register_azure_kv_provider()
is idempotent—you can call it during application
startup without worrying about duplicate work.
3. Resolve a secret URI¶
import envresolve
envresolve.register_azure_kv_provider()
# Plain strings are returned unchanged (idempotent behaviour)
print(envresolve.resolve_secret("db-password")) # db-password
# Secret URIs fetch values from Azure Key Vault
password = envresolve.resolve_secret("akv://corp-vault/db-password")
print(password)
Iterative resolution¶
resolve_secret()
keeps resolving until the returned value is stable. This lets
you chain indirections or mix URI results with variable expansion:
import os
import envresolve
envresolve.register_azure_kv_provider()
os.environ["ENVIRONMENT"] = "prod"
# akv://config/service → "akv://vault-${ENVIRONMENT}/service"
secret = envresolve.resolve_secret("akv://config/service")
print(secret) # Resolved value from akv://vault-prod/service
Loading and exporting from .env
¶
import os
import envresolve
envresolve.register_azure_kv_provider()
# .env may contain plain values, variable references, and akv:// URIs
resolved = envresolve.load_env(".env", export=True)
print(resolved["DB_PASSWORD"])
print(os.environ["DB_PASSWORD"]) # Exported unless override=False and already set
Use export=False
when you only need the resolved dictionary, or set
override=True
if you want to intentionally replace existing os.environ
values.
Error Handling¶
When working with external services, it's important to handle potential errors like missing dependencies, incorrect configuration, or network issues.
Provider and Resolution Errors¶
Here is a robust example of how to handle errors during provider registration and secret resolution:
import envresolve
from envresolve.exceptions import ProviderRegistrationError, SecretResolutionError
try:
# This might fail if 'envresolve[azure]' is not installed
envresolve.register_azure_kv_provider()
# This might fail due to permissions, network issues, or if the secret doesn't exist
secret_value = envresolve.resolve_secret("akv://corp-vault/db-password")
print(secret_value)
except ProviderRegistrationError as e:
print(f"Provider setup failed: {e}")
# Example: Provider setup failed: Azure Key Vault provider requires: azure-identity, azure-keyvault-secrets. Install with: pip install envresolve[azure]
except SecretResolutionError as e:
print(f"Failed to fetch secret: {e}")
This pattern ensures that your application can gracefully handle both setup-time (missing dependencies) and run-time (secret access) errors.
Circular Reference Detection¶
envresolve automatically detects circular references and raises a clear error:
from envresolve import expand_variables
from envresolve.exceptions import CircularReferenceError
env = {
"A": "${B}",
"B": "${A}"
}
try:
result = expand_variables(env["A"], env)
except CircularReferenceError as e:
print(f"Error: {e}")
# Error: Circular reference detected: B -> A -> B
# Inspect the exact cycle if you need more detail
print(e.chain) # ['B', 'A', 'B']
Missing Variable Error¶
If a referenced variable doesn't exist, VariableNotFoundError
is raised:
from envresolve import expand_variables
from envresolve.exceptions import VariableNotFoundError
env = {"A": "value"}
try:
result = expand_variables("${MISSING}", env)
except VariableNotFoundError as e:
print(f"Error: {e}")
# Error: Variable 'MISSING' not found
Advanced Use Cases¶
Building Secret URIs Dynamically¶
from envresolve import expand_variables
# Define vault and environment once
env = {
"ENVIRONMENT": "production",
"VAULT": "${ENVIRONMENT}-keyvault",
# Define all secrets using the vault
"DB_HOST_URI": "akv://${VAULT}/db-host",
"DB_USER_URI": "akv://${VAULT}/db-user",
"DB_PASS_URI": "akv://${VAULT}/db-password",
"API_KEY_URI": "akv://${VAULT}/api-key",
}
# Expand each URI
for key in ["DB_HOST_URI", "DB_USER_URI", "DB_PASS_URI", "API_KEY_URI"]:
expanded = expand_variables(env[key], env)
print(f"{key}: {expanded}")
# Output:
# DB_HOST_URI: akv://production-keyvault/db-host
# DB_USER_URI: akv://production-keyvault/db-user
# DB_PASS_URI: akv://production-keyvault/db-password
# API_KEY_URI: akv://production-keyvault/api-key
Plain Text Pass-Through¶
Text without variable references is returned unchanged:
from envresolve import expand_variables
result = expand_variables("plain text with $100 price", {"VAR": "value"})
print(result) # Output: plain text with $100 price
Note: A lone $
followed by non-variable characters (like digits) is preserved.
Secret Resolution Errors¶
Azure-specific failures (missing vaults, permission issues, network errors)
raise SecretResolutionError
. The exception carries the failing URI, making it
easy to log or surface to users:
import envresolve
from envresolve.exceptions import SecretResolutionError
envresolve.register_azure_kv_provider()
try:
envresolve.resolve_secret("akv://missing-vault/api-key")
except SecretResolutionError as exc:
print(exc) # Human-readable message
print(exc.uri) # akv://missing-vault/api-key
print(exc.original_error) # Underlying Azure exception (if available)