0
# Configuration Management
1
2
The configuration system provides comprehensive configuration file handling with support for multiple formats, default values, and hierarchical configuration merging. It integrates seamlessly with argument parsing for unified application configuration.
3
4
## Capabilities
5
6
### Configuration Handler Interface
7
8
Base interface for configuration management handlers that defines the contract for configuration operations.
9
10
```python { .api }
11
class ConfigHandler:
12
"""
13
Configuration handler interface for managing application configuration.
14
15
Provides methods for parsing configuration files, merging configuration
16
data, and accessing configuration values in a structured way.
17
"""
18
19
def parse_file(self, file_path: str) -> bool:
20
"""
21
Parse and load a configuration file.
22
23
Args:
24
file_path: Path to configuration file to parse
25
26
Returns:
27
True if file was successfully parsed, False otherwise
28
"""
29
30
def merge(self, dict_obj: Dict[str, Any]) -> None:
31
"""
32
Merge a dictionary into the configuration.
33
34
Args:
35
dict_obj: Dictionary to merge into configuration
36
"""
37
38
def get(self, section: str, key: str) -> Any:
39
"""
40
Get a configuration value.
41
42
Args:
43
section: Configuration section name
44
key: Configuration key name
45
46
Returns:
47
Configuration value
48
49
Raises:
50
KeyError: If section or key not found
51
"""
52
53
def get_section_dict(self, section: str) -> Dict[str, Any]:
54
"""
55
Get an entire configuration section as a dictionary.
56
57
Args:
58
section: Section name to retrieve
59
60
Returns:
61
Dictionary containing all key-value pairs in the section
62
63
Raises:
64
KeyError: If section not found
65
"""
66
67
def get_sections(self) -> List[str]:
68
"""
69
Get list of all configuration sections.
70
71
Returns:
72
List of section names
73
"""
74
75
def add_section(self, section: str) -> None:
76
"""
77
Add a new configuration section.
78
79
Args:
80
section: Section name to add
81
"""
82
83
def has_section(self, section: str) -> bool:
84
"""
85
Check if a configuration section exists.
86
87
Args:
88
section: Section name to check
89
90
Returns:
91
True if section exists, False otherwise
92
"""
93
94
def keys(self, section: str) -> List[str]:
95
"""
96
Get list of keys in a configuration section.
97
98
Args:
99
section: Section name
100
101
Returns:
102
List of key names in the section
103
104
Raises:
105
KeyError: If section not found
106
"""
107
108
def set(self, section: str, key: str, value: Any) -> None:
109
"""
110
Set a configuration value.
111
112
Args:
113
section: Configuration section name
114
key: Configuration key name
115
value: Value to set
116
"""
117
```
118
119
### Configuration Helper Functions
120
121
Utility functions for working with configuration defaults and initialization.
122
123
```python { .api }
124
def init_defaults(*sections: str) -> Dict[str, Any]:
125
"""
126
Create a standard dictionary for application configuration defaults.
127
128
Creates a nested dictionary structure with the specified sections.
129
This is commonly used for setting up application configuration defaults.
130
131
Args:
132
*sections: Section names to create in the defaults dictionary
133
134
Returns:
135
Dictionary with nested sections for configuration defaults
136
137
Example:
138
config = init_defaults('myapp', 'database', 'logging')
139
# Returns: {'myapp': {}, 'database': {}, 'logging': {}}
140
"""
141
```
142
143
## Usage Examples
144
145
### Basic Configuration Setup
146
147
```python
148
from cement import App, init_defaults
149
150
# Initialize configuration defaults
151
CONFIG = init_defaults('myapp')
152
CONFIG['myapp']['debug'] = False
153
CONFIG['myapp']['log_level'] = 'INFO'
154
CONFIG['myapp']['max_connections'] = 100
155
156
class MyApp(App):
157
class Meta:
158
label = 'myapp'
159
config_defaults = CONFIG
160
config_files = [
161
'/etc/myapp.conf',
162
'~/.myapp.conf',
163
'./myapp.conf'
164
]
165
166
with MyApp() as app:
167
app.setup()
168
169
# Access configuration values
170
debug = app.config.get('myapp', 'debug')
171
log_level = app.config.get('myapp', 'log_level')
172
max_conn = app.config.get('myapp', 'max_connections')
173
174
print(f"Debug: {debug}")
175
print(f"Log Level: {log_level}")
176
print(f"Max Connections: {max_conn}")
177
```
178
179
### Configuration File Formats
180
181
#### ConfigParser Format (.conf, .ini)
182
183
```ini
184
# /etc/myapp.conf
185
[myapp]
186
debug = false
187
log_level = INFO
188
max_connections = 100
189
190
[database]
191
host = localhost
192
port = 5432
193
name = myapp_db
194
user = myapp_user
195
password = secret123
196
197
[logging]
198
file = /var/log/myapp.log
199
rotate = true
200
max_size = 10MB
201
```
202
203
#### YAML Format (with yaml extension)
204
205
```yaml
206
# ~/.myapp.yaml
207
myapp:
208
debug: false
209
log_level: INFO
210
max_connections: 100
211
212
database:
213
host: localhost
214
port: 5432
215
name: myapp_db
216
user: myapp_user
217
password: secret123
218
219
logging:
220
file: /var/log/myapp.log
221
rotate: true
222
max_size: 10MB
223
```
224
225
### Multi-Section Configuration
226
227
```python
228
from cement import App, init_defaults
229
230
# Initialize with multiple sections
231
CONFIG = init_defaults('myapp', 'database', 'logging', 'cache')
232
233
# Set defaults for each section
234
CONFIG['myapp']['debug'] = False
235
CONFIG['myapp']['version'] = '1.0.0'
236
237
CONFIG['database']['host'] = 'localhost'
238
CONFIG['database']['port'] = 5432
239
CONFIG['database']['timeout'] = 30
240
241
CONFIG['logging']['level'] = 'INFO'
242
CONFIG['logging']['file'] = None
243
CONFIG['logging']['format'] = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
244
245
CONFIG['cache']['enabled'] = True
246
CONFIG['cache']['ttl'] = 3600
247
CONFIG['cache']['max_size'] = 1000
248
249
class MyApp(App):
250
class Meta:
251
label = 'myapp'
252
config_defaults = CONFIG
253
config_files = ['./myapp.conf']
254
255
with MyApp() as app:
256
app.setup()
257
258
# Access different sections
259
db_config = app.config.get_section_dict('database')
260
print(f"Database config: {db_config}")
261
262
# Check if sections exist
263
if app.config.has_section('cache'):
264
cache_enabled = app.config.get('cache', 'enabled')
265
print(f"Cache enabled: {cache_enabled}")
266
267
# List all sections
268
sections = app.config.get_sections()
269
print(f"Available sections: {sections}")
270
```
271
272
### Dynamic Configuration Management
273
274
```python
275
from cement import App, Controller, ex, init_defaults
276
277
CONFIG = init_defaults('myapp')
278
279
class ConfigController(Controller):
280
class Meta:
281
label = 'config'
282
stacked_on = 'base'
283
stacked_type = 'nested'
284
285
@ex(
286
help='show configuration values',
287
arguments=[
288
(['--section'], {'help': 'show specific section only'})
289
]
290
)
291
def show(self):
292
"""Display current configuration."""
293
if self.app.pargs.section:
294
section = self.app.pargs.section
295
if self.app.config.has_section(section):
296
config_dict = self.app.config.get_section_dict(section)
297
print(f"[{section}]")
298
for key, value in config_dict.items():
299
print(f"{key} = {value}")
300
else:
301
print(f"Section '{section}' not found")
302
else:
303
# Show all sections
304
for section in self.app.config.get_sections():
305
print(f"[{section}]")
306
config_dict = self.app.config.get_section_dict(section)
307
for key, value in config_dict.items():
308
print(f"{key} = {value}")
309
print()
310
311
@ex(
312
help='set configuration value',
313
arguments=[
314
(['section'], {'help': 'configuration section'}),
315
(['key'], {'help': 'configuration key'}),
316
(['value'], {'help': 'configuration value'})
317
]
318
)
319
def set(self):
320
"""Set a configuration value."""
321
section = self.app.pargs.section
322
key = self.app.pargs.key
323
value = self.app.pargs.value
324
325
# Create section if it doesn't exist
326
if not self.app.config.has_section(section):
327
self.app.config.add_section(section)
328
329
self.app.config.set(section, key, value)
330
print(f"Set {section}.{key} = {value}")
331
332
@ex(
333
help='get configuration value',
334
arguments=[
335
(['section'], {'help': 'configuration section'}),
336
(['key'], {'help': 'configuration key'})
337
]
338
)
339
def get(self):
340
"""Get a configuration value."""
341
section = self.app.pargs.section
342
key = self.app.pargs.key
343
344
try:
345
value = self.app.config.get(section, key)
346
print(f"{section}.{key} = {value}")
347
except KeyError:
348
print(f"Configuration key '{section}.{key}' not found")
349
350
class BaseController(Controller):
351
class Meta:
352
label = 'base'
353
354
class MyApp(App):
355
class Meta:
356
label = 'myapp'
357
base_controller = 'base'
358
config_defaults = CONFIG
359
handlers = [
360
BaseController,
361
ConfigController
362
]
363
364
with MyApp() as app:
365
app.run()
366
367
# Usage:
368
# myapp config show
369
# myapp config show --section database
370
# myapp config set database host localhost
371
# myapp config get database host
372
```
373
374
### Configuration with Environment Variables
375
376
```python
377
import os
378
from cement import App, init_defaults
379
380
CONFIG = init_defaults('myapp')
381
382
# Set defaults that can be overridden by environment variables
383
CONFIG['myapp']['debug'] = os.getenv('MYAPP_DEBUG', 'false').lower() == 'true'
384
CONFIG['myapp']['log_level'] = os.getenv('MYAPP_LOG_LEVEL', 'INFO')
385
CONFIG['myapp']['database_url'] = os.getenv('DATABASE_URL', 'sqlite:///app.db')
386
387
class MyApp(App):
388
class Meta:
389
label = 'myapp'
390
config_defaults = CONFIG
391
392
def setup(self):
393
super().setup()
394
395
# Override with environment variables after config file loading
396
env_overrides = {}
397
398
if 'MYAPP_DEBUG' in os.environ:
399
env_overrides['debug'] = os.getenv('MYAPP_DEBUG').lower() == 'true'
400
401
if 'MYAPP_LOG_LEVEL' in os.environ:
402
env_overrides['log_level'] = os.getenv('MYAPP_LOG_LEVEL')
403
404
if 'DATABASE_URL' in os.environ:
405
env_overrides['database_url'] = os.getenv('DATABASE_URL')
406
407
# Merge environment overrides
408
if env_overrides:
409
self.config.merge({'myapp': env_overrides})
410
411
with MyApp() as app:
412
app.setup()
413
414
debug = app.config.get('myapp', 'debug')
415
log_level = app.config.get('myapp', 'log_level')
416
db_url = app.config.get('myapp', 'database_url')
417
418
print(f"Configuration loaded:")
419
print(f" Debug: {debug}")
420
print(f" Log Level: {log_level}")
421
print(f" Database URL: {db_url}")
422
```
423
424
### Configuration Validation
425
426
```python
427
from cement import App, init_defaults
428
429
CONFIG = init_defaults('myapp', 'database')
430
CONFIG['myapp']['name'] = 'MyApplication'
431
CONFIG['myapp']['version'] = '1.0.0'
432
CONFIG['database']['host'] = 'localhost'
433
CONFIG['database']['port'] = 5432
434
435
class MyApp(App):
436
class Meta:
437
label = 'myapp'
438
config_defaults = CONFIG
439
config_files = ['./myapp.conf']
440
441
def validate_config(self):
442
"""Validate configuration after loading."""
443
# Check required configuration values
444
required_keys = [
445
('myapp', 'name'),
446
('myapp', 'version'),
447
('database', 'host'),
448
('database', 'port')
449
]
450
451
for section, key in required_keys:
452
try:
453
value = self.config.get(section, key)
454
if value is None or value == '':
455
raise ValueError(f"Required configuration {section}.{key} is missing or empty")
456
except KeyError:
457
raise ValueError(f"Required configuration {section}.{key} is missing")
458
459
# Validate data types and ranges
460
port = self.config.get('database', 'port')
461
if not isinstance(port, int) or port < 1 or port > 65535:
462
raise ValueError(f"Database port must be an integer between 1 and 65535, got: {port}")
463
464
print("Configuration validation passed")
465
466
def setup(self):
467
super().setup()
468
self.validate_config()
469
470
with MyApp() as app:
471
app.setup()
472
app.run()
473
```
474
475
### Hierarchical Configuration Loading
476
477
```python
478
from cement import App, init_defaults
479
import os
480
481
CONFIG = init_defaults('myapp')
482
CONFIG['myapp']['environment'] = 'development'
483
484
class MyApp(App):
485
class Meta:
486
label = 'myapp'
487
config_defaults = CONFIG
488
489
def setup(self):
490
super().setup()
491
492
# Load configuration files in priority order
493
config_files = [
494
'/etc/myapp/config.conf', # System-wide config
495
'/etc/myapp/myapp.conf', # System-wide app config
496
os.path.expanduser('~/.myapp.conf'), # User config
497
'./myapp.conf', # Local config
498
]
499
500
# Add environment-specific config
501
env = self.config.get('myapp', 'environment')
502
env_config = f'./config/{env}.conf'
503
config_files.append(env_config)
504
505
# Load each config file (later files override earlier ones)
506
for config_file in config_files:
507
if os.path.exists(config_file):
508
print(f"Loading config: {config_file}")
509
self.config.parse_file(config_file)
510
511
with MyApp() as app:
512
app.setup()
513
514
# Final configuration reflects the hierarchy
515
environment = app.config.get('myapp', 'environment')
516
print(f"Running in {environment} environment")
517
518
app.run()
519
```