0
# Configuration Management
1
2
Invoke provides a sophisticated multi-source configuration system that supports files, environment variables, command-line arguments, and Python dictionaries with hierarchical merging and attribute-style access.
3
4
## Capabilities
5
6
### Config Class
7
8
Main configuration management system with multi-source loading and hierarchical merging.
9
10
```python { .api }
11
class Config:
12
"""
13
Configuration management with multi-source loading and merging.
14
15
Supports loading from:
16
- Default values
17
- System configuration files
18
- User configuration files
19
- Project configuration files
20
- Environment variables
21
- Runtime overrides
22
- Collection-specific configuration
23
24
Attributes:
25
- _defaults (dict): Default configuration values
26
- _overrides (dict): Runtime override values
27
- _system (dict): System-level configuration
28
- _user (dict): User-level configuration
29
- _project (dict): Project-level configuration
30
- _runtime (dict): Runtime configuration
31
- _collection (dict): Collection-specific configuration
32
- _env (dict): Environment variable configuration
33
"""
34
35
def __init__(self, defaults=None, overrides=None, lazy=False, runtime_path=None, system_prefix=None, user_prefix=None, project_location=None, env_prefix=None, file_prefix=None):
36
"""
37
Initialize Config object.
38
39
Parameters:
40
- defaults (dict, optional): Default configuration values
41
- overrides (dict, optional): Runtime override values
42
- lazy (bool): Whether to defer configuration loading
43
- runtime_path (str, optional): Runtime configuration file path
44
- system_prefix (str, optional): System config file prefix
45
- user_prefix (str, optional): User config file prefix
46
- project_location (str, optional): Project root directory
47
- env_prefix (str, optional): Environment variable prefix
48
- file_prefix (str, optional): Configuration file prefix
49
"""
50
51
@classmethod
52
def global_defaults(cls):
53
"""
54
Get global default configuration values.
55
56
Returns:
57
dict: Global default configuration
58
"""
59
60
def load_defaults(self, merge=True):
61
"""
62
Load default configuration values.
63
64
Parameters:
65
- merge (bool): Whether to merge with existing configuration
66
67
Returns:
68
Config: Self for method chaining
69
"""
70
71
def load_overrides(self, merge=True):
72
"""
73
Load runtime override configuration.
74
75
Parameters:
76
- merge (bool): Whether to merge with existing configuration
77
78
Returns:
79
Config: Self for method chaining
80
"""
81
82
def load_system(self, merge=True):
83
"""
84
Load system-level configuration files.
85
86
Parameters:
87
- merge (bool): Whether to merge with existing configuration
88
89
Returns:
90
Config: Self for method chaining
91
"""
92
93
def load_user(self, merge=True):
94
"""
95
Load user-level configuration files.
96
97
Parameters:
98
- merge (bool): Whether to merge with existing configuration
99
100
Returns:
101
Config: Self for method chaining
102
"""
103
104
def load_project(self, merge=True):
105
"""
106
Load project-level configuration files.
107
108
Parameters:
109
- merge (bool): Whether to merge with existing configuration
110
111
Returns:
112
Config: Self for method chaining
113
"""
114
115
def load_runtime(self, merge=True):
116
"""
117
Load runtime configuration file.
118
119
Parameters:
120
- merge (bool): Whether to merge with existing configuration
121
122
Returns:
123
Config: Self for method chaining
124
"""
125
126
def load_shell_env(self, merge=True):
127
"""
128
Load configuration from environment variables.
129
130
Parameters:
131
- merge (bool): Whether to merge with existing configuration
132
133
Returns:
134
Config: Self for method chaining
135
"""
136
137
def load_collection(self, collection, merge=True):
138
"""
139
Load collection-specific configuration.
140
141
Parameters:
142
- collection (Collection): Task collection
143
- merge (bool): Whether to merge with existing configuration
144
145
Returns:
146
Config: Self for method chaining
147
"""
148
149
def set_project_location(self, path):
150
"""
151
Set project root directory for configuration files.
152
153
Parameters:
154
- path (str): Project root directory path
155
"""
156
157
def set_runtime_path(self, path):
158
"""
159
Set runtime configuration file path.
160
161
Parameters:
162
- path (str): Runtime configuration file path
163
"""
164
165
def merge(self, *dicts):
166
"""
167
Merge configuration dictionaries hierarchically.
168
169
Parameters:
170
- *dicts: Configuration dictionaries to merge
171
172
Returns:
173
Config: New merged configuration
174
"""
175
176
def clone(self, into=None):
177
"""
178
Create a copy of this configuration.
179
180
Parameters:
181
- into (type, optional): Class to clone into
182
183
Returns:
184
Config: Cloned configuration object
185
"""
186
```
187
188
### DataProxy Class
189
190
Base class providing nested dictionary access with attribute-style syntax.
191
192
```python { .api }
193
class DataProxy:
194
"""
195
Nested dictionary with attribute-style access.
196
197
Provides both dict-style (obj['key']) and attribute-style (obj.key)
198
access to nested configuration data.
199
"""
200
201
@classmethod
202
def from_data(cls, data):
203
"""
204
Create DataProxy from dictionary data.
205
206
Parameters:
207
- data (dict): Dictionary data to wrap
208
209
Returns:
210
DataProxy: Proxy object for the data
211
"""
212
213
def __getitem__(self, key):
214
"""Get item using dict-style access."""
215
216
def __setitem__(self, key, value):
217
"""Set item using dict-style access."""
218
219
def __delitem__(self, key):
220
"""Delete item using dict-style access."""
221
222
def __contains__(self, key):
223
"""Test membership using 'in' operator."""
224
225
def __iter__(self):
226
"""Iterate over keys."""
227
228
def __len__(self):
229
"""Get number of top-level keys."""
230
231
def __getattr__(self, key):
232
"""Get item using attribute-style access."""
233
234
def __setattr__(self, key, value):
235
"""Set item using attribute-style access."""
236
237
def get(self, key, default=None):
238
"""Get item with default value."""
239
240
def pop(self, key, *args):
241
"""Remove and return item."""
242
243
def popitem(self):
244
"""Remove and return arbitrary item."""
245
246
def clear(self):
247
"""Remove all items."""
248
249
def update(self, other):
250
"""Update with items from another mapping."""
251
252
def keys(self):
253
"""Get view of keys."""
254
255
def values(self):
256
"""Get view of values."""
257
258
def items(self):
259
"""Get view of key-value pairs."""
260
```
261
262
### Configuration Utility Functions
263
264
Helper functions for configuration manipulation.
265
266
```python { .api }
267
def merge_dicts(base, updates):
268
"""
269
Recursively merge configuration dictionaries.
270
271
Parameters:
272
- base (dict): Base configuration dictionary
273
- updates (dict): Updates to apply
274
275
Returns:
276
dict: Merged configuration dictionary
277
"""
278
279
def copy_dict(source):
280
"""
281
Deep copy a configuration dictionary.
282
283
Parameters:
284
- source (dict): Dictionary to copy
285
286
Returns:
287
dict: Deep copy of source dictionary
288
"""
289
290
def excise(dict_, keypath):
291
"""
292
Remove nested key from dictionary.
293
294
Parameters:
295
- dict_ (dict): Dictionary to modify
296
- keypath (str): Dot-separated key path (e.g., 'section.subsection.key')
297
298
Returns:
299
any: Removed value
300
"""
301
302
def obliterate(base, deletions):
303
"""
304
Remove multiple nested keys from dictionary.
305
306
Parameters:
307
- base (dict): Dictionary to modify
308
- deletions (list): List of dot-separated key paths to remove
309
310
Returns:
311
dict: Modified dictionary
312
"""
313
```
314
315
## Usage Examples
316
317
### Basic Configuration Usage
318
319
```python
320
from invoke import Config
321
322
# Create configuration with defaults
323
config = Config({
324
'run': {
325
'echo': True,
326
'pty': False
327
},
328
'sudo': {
329
'password': None
330
}
331
})
332
333
# Access configuration values
334
print(config.run.echo) # True
335
print(config['run']['pty']) # False
336
337
# Modify configuration
338
config.run.timeout = 30
339
config['sudo']['password'] = 'secret'
340
```
341
342
### Multi-source Configuration Loading
343
344
```python
345
from invoke import Config
346
347
# Load from all default sources
348
config = Config()
349
config.load_defaults()
350
config.load_system() # /etc/invoke.yaml
351
config.load_user() # ~/.invoke.yaml
352
config.load_project() # ./invoke.yaml
353
config.load_shell_env() # Environment variables
354
355
# Check final configuration
356
print(f"Echo enabled: {config.run.echo}")
357
```
358
359
### Configuration Files
360
361
Create configuration files in YAML or JSON format:
362
363
**invoke.yaml**:
364
```yaml
365
run:
366
echo: true
367
pty: false
368
warn: false
369
370
sudo:
371
password: null
372
prompt_for_password: true
373
374
tasks:
375
auto_dash_names: true
376
collection_name: 'tasks'
377
```
378
379
**invoke.json**:
380
```json
381
{
382
"run": {
383
"echo": true,
384
"pty": false,
385
"shell": "/bin/bash"
386
},
387
"sudo": {
388
"password": null
389
}
390
}
391
```
392
393
### Environment Variable Configuration
394
395
```python
396
import os
397
from invoke import Config
398
399
# Set environment variables (prefix with INVOKE_)
400
os.environ['INVOKE_RUN_ECHO'] = 'false'
401
os.environ['INVOKE_RUN_PTY'] = 'true'
402
os.environ['INVOKE_SUDO_PASSWORD'] = 'mypassword'
403
404
# Load configuration including environment
405
config = Config()
406
config.load_defaults()
407
config.load_shell_env()
408
409
print(config.run.echo) # False (from env var)
410
print(config.run.pty) # True (from env var)
411
```
412
413
### Configuration Merging and Cloning
414
415
```python
416
from invoke import Config, merge_dicts
417
418
# Base configuration
419
base_config = Config({
420
'run': {'echo': True, 'pty': False},
421
'sudo': {'password': None}
422
})
423
424
# Override configuration
425
overrides = {
426
'run': {'pty': True, 'timeout': 30},
427
'new_section': {'enabled': True}
428
}
429
430
# Merge configurations
431
merged = base_config.merge(overrides)
432
print(merged.run.echo) # True (from base)
433
print(merged.run.pty) # True (from override)
434
print(merged.run.timeout) # 30 (from override)
435
436
# Clone configuration
437
cloned = base_config.clone()
438
cloned.run.echo = False
439
print(base_config.run.echo) # True (original unchanged)
440
print(cloned.run.echo) # False (clone modified)
441
```
442
443
### Context Integration
444
445
```python
446
from invoke import Context, Config
447
448
# Create custom configuration
449
config = Config({
450
'run': {
451
'echo': True,
452
'pty': True,
453
'shell': '/bin/zsh'
454
}
455
})
456
457
# Use with context
458
ctx = Context(config=config)
459
result = ctx.run("echo 'Hello World'") # Uses custom config
460
```
461
462
### Task-specific Configuration
463
464
```python
465
from invoke import task, Context
466
467
@task
468
def deploy(ctx, env='staging'):
469
"""Deploy with environment-specific configuration."""
470
471
# Access configuration for current environment
472
db_host = ctx.config.environments[env].database.host
473
api_key = ctx.config.environments[env].api_key
474
475
ctx.run(f"deploy.sh --host {db_host} --key {api_key}")
476
477
# Configuration file (invoke.yaml):
478
# environments:
479
# staging:
480
# database:
481
# host: staging-db.example.com
482
# api_key: staging-key
483
# production:
484
# database:
485
# host: prod-db.example.com
486
# api_key: prod-key
487
```
488
489
### Configuration Validation
490
491
```python
492
from invoke import Config
493
from invoke.exceptions import UncastableEnvVar, AmbiguousEnvVar
494
495
try:
496
config = Config()
497
config.load_shell_env()
498
except UncastableEnvVar as e:
499
print(f"Invalid environment variable: {e}")
500
except AmbiguousEnvVar as e:
501
print(f"Ambiguous environment variable: {e}")
502
```