0
# Configuration Management
1
2
Environment configuration utilities including .env file reading, variable expansion, prefixed parsing, validation management, and configuration export capabilities.
3
4
## Capabilities
5
6
### Environment File Loading
7
8
Read .env files into the environment with automatic discovery, recursion control, and override management.
9
10
```python { .api }
11
@staticmethod
12
def read_env(path=None, *, recurse=True, verbose=False, override=False, return_path=False):
13
"""
14
Read a .env file into os.environ.
15
16
Parameters:
17
- path: str or Path, specific .env file path (optional, defaults to search)
18
- recurse: bool, whether to search parent directories (default: True)
19
- verbose: bool, whether to print loading information (default: False)
20
- override: bool, whether to override existing environment variables (default: False)
21
- return_path: bool, whether to return the path of loaded file (default: False)
22
23
Returns:
24
bool or str: Success status or file path if return_path=True
25
26
Notes:
27
When path is None, searches for .env starting from caller's directory.
28
With recurse=True, walks up directory tree until .env is found.
29
"""
30
```
31
32
Usage examples:
33
34
```python
35
from environs import Env, env
36
37
# Load .env from current directory or parents
38
env.read_env()
39
40
# Load specific .env file
41
env.read_env("/path/to/specific/.env")
42
43
# Load with verbose output
44
env.read_env(verbose=True)
45
46
# Override existing environment variables
47
env.read_env(override=True)
48
49
# Get the path of loaded file
50
env_path = env.read_env(return_path=True)
51
print(f"Loaded environment from: {env_path}")
52
53
# Class method usage
54
success = Env.read_env("./config/.env")
55
if success:
56
print("Environment file loaded successfully")
57
```
58
59
### Prefixed Environment Variables
60
61
Parse environment variables with common prefixes using context manager for clean scoping.
62
63
```python { .api }
64
def prefixed(self, prefix: str):
65
"""
66
Context manager for parsing environment variables with a common prefix.
67
68
Parameters:
69
- prefix: str, prefix to prepend to all variable names within context
70
71
Returns:
72
Iterator[Env]: Context manager yielding the current Env instance
73
74
Notes:
75
Prefixes can be nested. The context manager automatically manages
76
prefix state and restores previous prefix on exit.
77
"""
78
```
79
80
Usage examples:
81
82
```python
83
import os
84
from environs import env
85
86
# Set prefixed environment variables
87
os.environ["DB_HOST"] = "localhost"
88
os.environ["DB_PORT"] = "5432"
89
os.environ["DB_NAME"] = "myapp"
90
os.environ["REDIS_HOST"] = "redis.example.com"
91
os.environ["REDIS_PORT"] = "6379"
92
93
# Parse with prefix
94
with env.prefixed("DB_"):
95
host = env.str("HOST") # Reads DB_HOST
96
port = env.int("PORT") # Reads DB_PORT
97
name = env.str("NAME") # Reads DB_NAME
98
99
print(f"Database: {name} at {host}:{port}")
100
101
# Nested prefixes
102
os.environ["API_V1_ENDPOINT"] = "https://api.v1.example.com"
103
os.environ["API_V1_TIMEOUT"] = "30"
104
105
with env.prefixed("API_"):
106
with env.prefixed("V1_"):
107
endpoint = env.str("ENDPOINT") # Reads API_V1_ENDPOINT
108
timeout = env.int("TIMEOUT") # Reads API_V1_TIMEOUT
109
110
# Multiple prefix contexts
111
def load_db_config():
112
with env.prefixed("DB_"):
113
return {
114
"host": env.str("HOST", "localhost"),
115
"port": env.int("PORT", 5432),
116
"name": env.str("NAME", "app"),
117
"user": env.str("USER", "app"),
118
"password": env.str("PASSWORD")
119
}
120
121
def load_redis_config():
122
with env.prefixed("REDIS_"):
123
return {
124
"host": env.str("HOST", "localhost"),
125
"port": env.int("PORT", 6379),
126
"db": env.int("DB", 0)
127
}
128
129
db_config = load_db_config()
130
redis_config = load_redis_config()
131
```
132
133
### Variable Expansion
134
135
Expand environment variables using `${VAR}` and `${VAR:-default}` syntax for dynamic configuration.
136
137
```python { .api }
138
# Variable expansion is enabled via constructor parameter
139
def __init__(self, *, expand_vars=False, **kwargs):
140
"""
141
Enable variable expansion in environment values.
142
143
Parameters:
144
- expand_vars: bool, whether to expand ${VAR} syntax (default: False)
145
146
Expansion syntax:
147
- ${VAR}: Expand to value of VAR, error if not set
148
- ${VAR:-default}: Expand to value of VAR, or 'default' if not set
149
- \\$: Escape $ character to prevent expansion
150
"""
151
```
152
153
Usage examples:
154
155
```python
156
import os
157
from environs import Env
158
159
# Set up base environment variables
160
os.environ["BASE_URL"] = "https://api.example.com"
161
os.environ["API_VERSION"] = "v1"
162
os.environ["USER_NAME"] = "john_doe"
163
164
# Environment variables with expansion syntax
165
os.environ["API_ENDPOINT"] = "${BASE_URL}/${API_VERSION}"
166
os.environ["USER_PROFILE_URL"] = "${BASE_URL}/${API_VERSION}/users/${USER_NAME}"
167
os.environ["BACKUP_PATH"] = "/backups/${USER_NAME:-anonymous}"
168
os.environ["LOG_FILE"] = "/var/log/${APP_NAME:-myapp}.log"
169
170
# Create Env with expansion enabled
171
env = Env(expand_vars=True)
172
173
# Parse expanded variables
174
api_endpoint = env.str("API_ENDPOINT") # => "https://api.example.com/v1"
175
profile_url = env.str("USER_PROFILE_URL") # => "https://api.example.com/v1/users/john_doe"
176
backup_path = env.str("BACKUP_PATH") # => "/backups/john_doe"
177
log_file = env.str("LOG_FILE") # => "/var/log/myapp.log" (uses default)
178
179
# Escaped variables
180
os.environ["LITERAL_DOLLAR"] = "Price: \\$19.99"
181
price = env.str("LITERAL_DOLLAR") # => "Price: $19.99"
182
```
183
184
### Validation and Sealing
185
186
Manage deferred validation and prevent further parsing after configuration is complete.
187
188
```python { .api }
189
def seal(self):
190
"""
191
Validate all parsed values and prevent new values from being added.
192
193
Raises:
194
EnvValidationError: If any parsed values failed validation
195
196
Notes:
197
After sealing, attempting to parse new variables raises EnvSealedError.
198
Useful for ensuring configuration is complete and valid before app startup.
199
"""
200
```
201
202
Usage examples:
203
204
```python
205
import os
206
from environs import Env, EnvValidationError, EnvSealedError
207
208
# Create non-eager env for deferred validation
209
env = Env(eager=False)
210
211
# Set up environment with some invalid values
212
os.environ["VALID_PORT"] = "8080"
213
os.environ["INVALID_PORT"] = "not_a_number"
214
os.environ["VALID_TIMEOUT"] = "30"
215
216
# Parse variables (errors collected, not raised immediately)
217
valid_port = env.int("VALID_PORT") # Success
218
invalid_port = env.int("INVALID_PORT") # Error collected
219
valid_timeout = env.int("VALID_TIMEOUT") # Success
220
221
# Validate all at once
222
try:
223
env.seal()
224
print("All configuration valid!")
225
except EnvValidationError as e:
226
print(f"Configuration errors: {e.error_messages}")
227
# Handle validation errors appropriately
228
229
# After sealing, no new parsing allowed
230
try:
231
new_value = env.str("NEW_VAR")
232
except EnvSealedError:
233
print("Cannot parse new variables after sealing")
234
```
235
236
### Configuration Export
237
238
Export parsed configuration as a dictionary for serialization or debugging.
239
240
```python { .api }
241
def dump(self):
242
"""
243
Dump parsed environment variables to a dictionary.
244
245
Returns:
246
dict: Dictionary of parsed values with simple data types
247
248
Notes:
249
Complex objects are serialized to simple types (strings, numbers).
250
Useful for logging configuration or creating configuration backups.
251
"""
252
```
253
254
Usage examples:
255
256
```python
257
import os
258
import json
259
from environs import env
260
from datetime import datetime
261
262
# Set up various environment variables
263
os.environ["APP_NAME"] = "MyApp"
264
os.environ["DEBUG"] = "true"
265
os.environ["PORT"] = "8080"
266
os.environ["CREATED_AT"] = "2023-12-25T10:30:00"
267
os.environ["FEATURES"] = "auth,logging,metrics"
268
269
# Parse various types
270
app_name = env.str("APP_NAME")
271
debug = env.bool("DEBUG")
272
port = env.int("PORT")
273
created_at = env.datetime("CREATED_AT")
274
features = env.list("FEATURES")
275
276
# Export configuration
277
config_dict = env.dump()
278
print("Current configuration:")
279
print(json.dumps(config_dict, indent=2, default=str))
280
281
# Save configuration to file
282
with open("config_backup.json", "w") as f:
283
json.dump(config_dict, f, indent=2, default=str)
284
285
# Use for debugging
286
def log_configuration():
287
config = env.dump()
288
print("Application started with configuration:")
289
for key, value in config.items():
290
# Don't log sensitive values
291
if "password" in key.lower() or "secret" in key.lower():
292
print(f" {key}: [REDACTED]")
293
else:
294
print(f" {key}: {value}")
295
```
296
297
### Initialization Options
298
299
Configure environment parsing behavior through constructor parameters.
300
301
```python { .api }
302
def __init__(self, *, eager=True, expand_vars=False, prefix=None):
303
"""
304
Initialize environment parser with behavior options.
305
306
Parameters:
307
- eager: bool, whether to raise validation errors immediately (default: True)
308
- expand_vars: bool, whether to expand ${VAR} syntax (default: False)
309
- prefix: str, global prefix for all variable names (default: None)
310
"""
311
```
312
313
Usage examples:
314
315
```python
316
from environs import Env
317
318
# Eager validation (default) - errors raised immediately
319
eager_env = Env(eager=True)
320
321
# Deferred validation - collect errors for later handling
322
deferred_env = Env(eager=False)
323
324
# Variable expansion enabled
325
expanding_env = Env(expand_vars=True)
326
327
# Global prefix
328
prefixed_env = Env(prefix="MYAPP_")
329
330
# Combined options
331
flexible_env = Env(
332
eager=False,
333
expand_vars=True,
334
prefix="APP_"
335
)
336
337
# Example with deferred validation
338
import os
339
os.environ["INVALID_NUMBER"] = "not_a_number"
340
341
deferred = Env(eager=False)
342
value = deferred.int("INVALID_NUMBER") # No error raised yet
343
344
try:
345
deferred.seal() # Now validation errors are raised
346
except EnvValidationError as e:
347
print(f"Validation failed: {e.error_messages}")
348
```
349
350
## Types
351
352
```python { .api }
353
from typing import Dict, Any, Union, Optional, Iterator
354
from pathlib import Path
355
356
ErrorMapping = Dict[str, List[str]]
357
ConfigDict = Dict[str, Any]
358
```