0
# Configuration System
1
2
Mopidy's configuration system provides schema-based configuration management with validation, type safety, and support for multiple configuration sources. It enables both core functionality and extensions to define, validate, and access configuration settings in a consistent manner.
3
4
## Capabilities
5
6
### Configuration Loading and Management
7
8
Core functions for loading, parsing, and managing configuration from multiple sources.
9
10
```python { .api }
11
def load(files, ext_schemas, ext_defaults, overrides):
12
"""
13
Load configuration from multiple sources.
14
15
Parameters:
16
- files (list[str]): Configuration file paths
17
- ext_schemas (list[ConfigSchema]): Extension schemas
18
- ext_defaults (list[str]): Extension default configs
19
- overrides (list[tuple]): Command-line overrides
20
21
Returns:
22
- tuple[dict, dict]: (config, errors) - Parsed config and validation errors
23
"""
24
...
25
26
def format(config, ext_schemas, comments=None, display=True):
27
"""
28
Format configuration for display or output.
29
30
Parameters:
31
- config (dict): Configuration to format
32
- ext_schemas (list[ConfigSchema]): Extension schemas
33
- comments (dict, optional): Comments to include
34
- display (bool): Whether to format for display
35
36
Returns:
37
- str: Formatted configuration string
38
"""
39
...
40
41
def format_initial(extensions_data):
42
"""
43
Format initial configuration template with defaults.
44
45
Parameters:
46
- extensions_data (list[ExtensionData]): Extension data
47
48
Returns:
49
- str: Initial configuration template
50
"""
51
...
52
53
def read(config_file):
54
"""
55
Read configuration file content.
56
57
Parameters:
58
- config_file (str): Path to configuration file
59
60
Returns:
61
- str: Configuration file content
62
"""
63
...
64
```
65
66
Usage example:
67
```python
68
from mopidy import config
69
70
# Load configuration
71
config_data, errors = config.load(
72
files=["/etc/mopidy/mopidy.conf", "~/.config/mopidy/mopidy.conf"],
73
ext_schemas=[spotify_schema, local_schema],
74
ext_defaults=[spotify_defaults, local_defaults],
75
overrides=[("core", "cache_dir", "/tmp/mopidy")]
76
)
77
78
if errors:
79
print("Configuration errors:", errors)
80
```
81
82
### Configuration Schemas
83
84
Schema definition system for validating and organizing configuration sections.
85
86
```python { .api }
87
class ConfigSchema:
88
"""
89
Configuration schema for a section.
90
91
Parameters:
92
- name (str): Schema section name
93
"""
94
def __init__(self, name): ...
95
96
name: str # Section name
97
98
def __setitem__(self, key, value):
99
"""
100
Add configuration field to schema.
101
102
Parameters:
103
- key (str): Configuration key
104
- value (ConfigValue): Field validator
105
"""
106
...
107
108
def __getitem__(self, key):
109
"""Get configuration field validator."""
110
...
111
112
def deserialize(self, values):
113
"""
114
Deserialize configuration values.
115
116
Parameters:
117
- values (dict): Raw configuration values
118
119
Returns:
120
- tuple[dict, dict]: (parsed_values, errors)
121
"""
122
...
123
124
def serialize(self, values, display=False):
125
"""
126
Serialize configuration values.
127
128
Parameters:
129
- values (dict): Configuration values to serialize
130
- display (bool): Whether formatting for display
131
132
Returns:
133
- dict: Serialized values
134
"""
135
...
136
137
class MapConfigSchema(ConfigSchema):
138
"""
139
Schema for map-like configuration sections.
140
141
Parameters:
142
- name (str): Schema section name
143
- value_type (ConfigValue): Validator for all values
144
"""
145
def __init__(self, name, value_type): ...
146
```
147
148
Usage example:
149
```python
150
from mopidy.config.schemas import ConfigSchema
151
from mopidy.config.types import String, Integer, Boolean, Secret
152
153
# Define schema for an extension
154
schema = ConfigSchema("spotify")
155
schema["username"] = String()
156
schema["password"] = Secret() # Hidden in output
157
schema["enabled"] = Boolean()
158
schema["timeout"] = Integer(minimum=1, maximum=300)
159
schema["playlists"] = List()
160
```
161
162
### Configuration Value Types
163
164
Strongly-typed configuration value validators with built-in validation and conversion.
165
166
```python { .api }
167
class ConfigValue:
168
"""Base class for configuration value types."""
169
170
def __init__(self, optional=False):
171
"""
172
Initialize config value.
173
174
Parameters:
175
- optional (bool): Whether value is optional
176
"""
177
...
178
179
def deserialize(self, value):
180
"""
181
Convert string value to Python type.
182
183
Parameters:
184
- value (str): Raw configuration value
185
186
Returns:
187
- tuple[Any, str]: (converted_value, error_message)
188
"""
189
...
190
191
def serialize(self, value, display=False):
192
"""
193
Convert Python value to string.
194
195
Parameters:
196
- value: Python value to serialize
197
- display (bool): Whether formatting for display
198
199
Returns:
200
- str: Serialized value
201
"""
202
...
203
204
class String(ConfigValue):
205
"""String configuration value."""
206
def __init__(self, optional=False, choices=None): ...
207
208
class Integer(ConfigValue):
209
"""
210
Integer configuration value.
211
212
Parameters:
213
- optional (bool): Whether value is optional
214
- minimum (int, optional): Minimum allowed value
215
- maximum (int, optional): Maximum allowed value
216
"""
217
def __init__(self, optional=False, minimum=None, maximum=None): ...
218
219
class Float(ConfigValue):
220
"""
221
Float configuration value.
222
223
Parameters:
224
- optional (bool): Whether value is optional
225
- minimum (float, optional): Minimum allowed value
226
- maximum (float, optional): Maximum allowed value
227
"""
228
def __init__(self, optional=False, minimum=None, maximum=None): ...
229
230
class Boolean(ConfigValue):
231
"""Boolean configuration value (true/false, yes/no, 1/0)."""
232
def __init__(self, optional=False): ...
233
234
class List(ConfigValue):
235
"""
236
List configuration value (comma-separated).
237
238
Parameters:
239
- optional (bool): Whether value is optional
240
- separator (str): List item separator (default: comma)
241
"""
242
def __init__(self, optional=False, separator=","): ...
243
244
class Pair(ConfigValue):
245
"""
246
Key-value pair configuration value.
247
248
Parameters:
249
- optional (bool): Whether value is optional
250
- separator (str): Key-value separator (default: pipe)
251
"""
252
def __init__(self, optional=False, separator="|"): ...
253
254
class Secret(ConfigValue):
255
"""Secret configuration value (hidden in output)."""
256
def __init__(self, optional=False): ...
257
258
class Path(ConfigValue):
259
"""File system path configuration value."""
260
def __init__(self, optional=False): ...
261
262
class Hostname(ConfigValue):
263
"""Network hostname configuration value."""
264
def __init__(self, optional=False): ...
265
266
class Port(ConfigValue):
267
"""
268
Network port configuration value.
269
270
Parameters:
271
- optional (bool): Whether value is optional
272
- choices (list[int], optional): Allowed port numbers
273
"""
274
def __init__(self, optional=False, choices=None): ...
275
276
class LogLevel(ConfigValue):
277
"""Logging level configuration value."""
278
def __init__(self, optional=False): ...
279
280
class LogColor(ConfigValue):
281
"""Log color configuration value."""
282
def __init__(self, optional=False): ...
283
```
284
285
Usage example:
286
```python
287
# Configure different value types
288
schema["server_host"] = Hostname()
289
schema["server_port"] = Port(choices=[8080, 8081, 8082])
290
schema["log_level"] = LogLevel()
291
schema["api_timeout"] = Integer(minimum=1, maximum=300)
292
schema["cache_dir"] = Path()
293
schema["enabled_formats"] = List()
294
schema["database_url"] = Secret() # Won't be shown in config output
295
```
296
297
### Deprecated Configuration Handling
298
299
Support for handling deprecated configuration options with warnings and migration.
300
301
```python { .api }
302
class Deprecated(ConfigValue):
303
"""Marks a configuration option as deprecated."""
304
def __init__(self, message=None): ...
305
306
class DeprecatedValue:
307
"""Wrapper for deprecated configuration values."""
308
def __init__(self, value, message): ...
309
```
310
311
Usage example:
312
```python
313
# Mark old configuration options as deprecated
314
schema["old_option"] = Deprecated("Use 'new_option' instead")
315
schema["legacy_setting"] = Deprecated()
316
```
317
318
### Configuration Proxy
319
320
Proxy object providing convenient access to nested configuration values.
321
322
```python { .api }
323
class Proxy:
324
"""
325
Configuration proxy for convenient access to nested values.
326
327
Parameters:
328
- data (dict): Configuration data to wrap
329
"""
330
def __init__(self, data): ...
331
332
def __getitem__(self, key): ...
333
def __iter__(self): ...
334
def __len__(self): ...
335
def __repr__(self): ...
336
```
337
338
Usage example:
339
```python
340
config_proxy = Proxy(config_data)
341
342
# Access nested configuration values
343
cache_dir = config_proxy["core"]["cache_dir"]
344
spotify_username = config_proxy["spotify"]["username"]
345
346
# Proxy maintains dict-like interface
347
for section_name in config_proxy:
348
section = config_proxy[section_name]
349
print(f"Section {section_name} has {len(section)} options")
350
```
351
352
### Core Configuration Schemas
353
354
Built-in configuration schemas for Mopidy's core functionality.
355
356
```python { .api }
357
# Core system configuration
358
_core_schema = ConfigSchema("core")
359
_core_schema["cache_dir"] = Path()
360
_core_schema["config_dir"] = Path()
361
_core_schema["data_dir"] = Path()
362
_core_schema["max_tracklist_length"] = Integer(minimum=1)
363
_core_schema["restore_state"] = Boolean(optional=True)
364
365
# Logging configuration
366
_logging_schema = ConfigSchema("logging")
367
_logging_schema["verbosity"] = Integer(minimum=-1, maximum=4)
368
_logging_schema["format"] = String()
369
_logging_schema["color"] = Boolean()
370
_logging_schema["config_file"] = Path(optional=True)
371
372
# Audio system configuration
373
_audio_schema = ConfigSchema("audio")
374
_audio_schema["mixer"] = String()
375
_audio_schema["mixer_volume"] = Integer(optional=True, minimum=0, maximum=100)
376
_audio_schema["output"] = String()
377
_audio_schema["buffer_time"] = Integer(optional=True, minimum=1)
378
379
# HTTP proxy configuration
380
_proxy_schema = ConfigSchema("proxy")
381
_proxy_schema["scheme"] = String(optional=True, choices=["http", "https", "socks4", "socks5"])
382
_proxy_schema["hostname"] = Hostname(optional=True)
383
_proxy_schema["port"] = Port(optional=True)
384
_proxy_schema["username"] = String(optional=True)
385
_proxy_schema["password"] = Secret(optional=True)
386
387
# Per-module log levels
388
_loglevels_schema = MapConfigSchema("loglevels", LogLevel())
389
390
# Per-module log colors
391
_logcolors_schema = MapConfigSchema("logcolors", LogColor())
392
```
393
394
### Configuration File Locations
395
396
Standard configuration file search paths and loading order:
397
398
```python { .api }
399
# Standard configuration locations (in search order):
400
# 1. /etc/mopidy/mopidy.conf
401
# 2. /etc/mopidy/conf.d/*.conf
402
# 3. $XDG_CONFIG_HOME/mopidy/mopidy.conf (defaults to ~/.config/mopidy/mopidy.conf)
403
# 4. $XDG_CONFIG_HOME/mopidy/conf.d/*.conf
404
# 5. Command-line overrides
405
406
# Configuration can also be loaded from directories:
407
config_data, errors = config.load(
408
files=[
409
"/etc/mopidy/mopidy.conf",
410
"/etc/mopidy/conf.d/", # Will load all .conf files
411
"~/.config/mopidy/mopidy.conf"
412
],
413
ext_schemas=schemas,
414
ext_defaults=defaults,
415
overrides=[]
416
)
417
```
418
419
### Configuration Validation and Error Handling
420
421
Comprehensive validation with detailed error reporting:
422
423
```python { .api }
424
# Configuration loading returns both config and errors
425
config_data, errors = config.load(files, schemas, defaults, overrides)
426
427
# Errors are organized by section
428
if errors:
429
for section_name, section_errors in errors.items():
430
print(f"Errors in [{section_name}]:")
431
for field, error_msg in section_errors.items():
432
print(f" {field}: {error_msg}")
433
434
# Example error output:
435
# Errors in [spotify]:
436
# username: Required field is missing
437
# timeout: Value must be between 1 and 300
438
```
439
440
### Environment Integration
441
442
Integration with environment variables and system paths:
443
444
```python { .api }
445
import os
446
from pathlib import Path
447
448
# Environment variable support in configuration
449
config_template = """
450
[core]
451
cache_dir = ${XDG_CACHE_HOME}/mopidy
452
data_dir = ${XDG_DATA_HOME}/mopidy
453
454
[myext]
455
api_key = ${MYEXT_API_KEY}
456
"""
457
458
# Path expansion and validation
459
path_config = Path()
460
expanded_path = path_config.deserialize("~/music") # Expands to full path
461
```
462
463
## Configuration Best Practices
464
465
### Extension Configuration Design
466
467
```python
468
from mopidy.config.schemas import ConfigSchema
469
from mopidy.config.types import String, Integer, Boolean, Secret, List
470
471
class MyExtension(Extension):
472
def get_config_schema(self):
473
schema = super().get_config_schema()
474
475
# Required settings
476
schema["api_key"] = Secret() # Never shown in output
477
schema["endpoint"] = String()
478
479
# Optional settings with defaults
480
schema["timeout"] = Integer(minimum=1, maximum=300)
481
schema["max_results"] = Integer(minimum=1, maximum=1000)
482
schema["enabled"] = Boolean()
483
484
# List and choice settings
485
schema["supported_formats"] = List()
486
schema["quality"] = String(choices=["low", "medium", "high"])
487
488
return schema
489
490
def get_default_config(self):
491
return """\
492
[myext]
493
enabled = true
494
api_key =
495
endpoint = https://api.example.com
496
timeout = 30
497
max_results = 100
498
supported_formats = mp3, flac, ogg
499
quality = medium
500
"""
501
```
502
503
### Configuration Access in Extensions
504
505
```python
506
class MyBackend(Backend):
507
def __init__(self, config, audio):
508
super().__init__(config, audio)
509
510
# Access extension configuration
511
ext_config = config["myext"]
512
self.api_key = ext_config["api_key"]
513
self.endpoint = ext_config["endpoint"]
514
self.timeout = ext_config["timeout"]
515
516
# Validate required settings
517
if not self.api_key:
518
raise ExtensionError("MyExt requires api_key configuration")
519
```
520
521
### Dynamic Configuration Updates
522
523
```python
524
# Configuration can be reloaded at runtime
525
def reload_config():
526
new_config, errors = config.load(
527
files=config_files,
528
ext_schemas=schemas,
529
ext_defaults=defaults,
530
overrides=[]
531
)
532
533
if not errors:
534
# Update components with new configuration
535
update_backends(new_config)
536
update_frontends(new_config)
537
```