config: Move load, validate and overrides code into mopidy.config
This commit is contained in:
parent
a5f2dc924c
commit
467c8b34dc
@ -1,12 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import codecs
|
||||
import ConfigParser as configparser
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import signal
|
||||
import StringIO
|
||||
import sys
|
||||
|
||||
import gobject
|
||||
@ -29,12 +26,10 @@ sys.path.insert(
|
||||
|
||||
from mopidy import exceptions, ext
|
||||
from mopidy.audio import Audio
|
||||
from mopidy.config import default_config, config_schemas
|
||||
from mopidy import config as config_utils # TODO: cleanup
|
||||
from mopidy import config as config_lib
|
||||
from mopidy.core import Core
|
||||
from mopidy.utils import deps, log, path, process, versioning
|
||||
|
||||
|
||||
logger = logging.getLogger('mopidy.main')
|
||||
|
||||
|
||||
@ -51,16 +46,19 @@ def main():
|
||||
|
||||
try:
|
||||
create_file_structures()
|
||||
logging_config = load_config(config_files, config_overrides)
|
||||
logging_config = config_lib.load(config_files, config_overrides)
|
||||
log.setup_logging(
|
||||
logging_config, options.verbosity_level, options.save_debug_log)
|
||||
extensions = ext.load_extensions()
|
||||
raw_config = load_config(config_files, config_overrides, extensions)
|
||||
raw_config = config_lib.load(config_files, config_overrides, extensions)
|
||||
extensions = ext.filter_enabled_extensions(raw_config, extensions)
|
||||
config = validate_config(raw_config, config_schemas, extensions)
|
||||
config = config_lib.validate(
|
||||
raw_config, config_lib.config_schemas, extensions)
|
||||
log.setup_log_levels(config)
|
||||
check_old_locations()
|
||||
|
||||
# TODO: wrap config in RO proxy.
|
||||
|
||||
# Anything that wants to exit after this point must use
|
||||
# mopidy.utils.process.exit_process as actors have been started.
|
||||
audio = setup_audio(config)
|
||||
@ -83,9 +81,7 @@ def main():
|
||||
|
||||
def check_config_override(option, opt, override):
|
||||
try:
|
||||
section, remainder = override.split('/', 1)
|
||||
key, value = remainder.split('=', 1)
|
||||
return (section, key, value)
|
||||
return config_lib.parse_override(override)
|
||||
except ValueError:
|
||||
raise optparse.OptionValueError(
|
||||
'option %s: must have the format section/key=value' % opt)
|
||||
@ -181,73 +177,6 @@ def check_old_locations():
|
||||
'for further instructions.', old_settings_file)
|
||||
|
||||
|
||||
def load_config(files, overrides, extensions=None):
|
||||
parser = configparser.RawConfigParser()
|
||||
|
||||
files = [path.expand_path(f) for f in files]
|
||||
sources = ['builtin-defaults'] + files + ['command-line']
|
||||
logger.info('Loading config from: %s', ', '.join(sources))
|
||||
|
||||
# Read default core config
|
||||
parser.readfp(StringIO.StringIO(default_config))
|
||||
|
||||
# Read default extension config
|
||||
for extension in extensions or []:
|
||||
parser.readfp(StringIO.StringIO(extension.get_default_config()))
|
||||
|
||||
# Load config from a series of config files
|
||||
for filename in files:
|
||||
# TODO: if this is the initial load of logging config we might not have
|
||||
# a logger at this point, we might want to handle this better.
|
||||
try:
|
||||
filehandle = codecs.open(filename, encoding='utf-8')
|
||||
parser.readfp(filehandle)
|
||||
except IOError:
|
||||
logger.debug('Config file %s not found; skipping', filename)
|
||||
continue
|
||||
except UnicodeDecodeError:
|
||||
logger.error('Config file %s is not UTF-8 encoded', filename)
|
||||
sys.exit(1)
|
||||
|
||||
raw_config = {}
|
||||
for section in parser.sections():
|
||||
raw_config[section] = dict(parser.items(section))
|
||||
|
||||
for section, key, value in overrides or []:
|
||||
raw_config.setdefault(section, {})[key] = value
|
||||
|
||||
return raw_config
|
||||
|
||||
|
||||
def validate_config(raw_config, schemas, extensions=None):
|
||||
# Collect config schemas to validate against
|
||||
sections_and_schemas = schemas.items()
|
||||
for extension in extensions or []:
|
||||
sections_and_schemas.append(
|
||||
(extension.ext_name, extension.get_config_schema()))
|
||||
|
||||
# Get validated config
|
||||
config = {}
|
||||
errors = {}
|
||||
for section_name, schema in sections_and_schemas:
|
||||
if section_name not in raw_config:
|
||||
errors[section_name] = {section_name: 'section not found'}
|
||||
try:
|
||||
items = raw_config[section_name].items()
|
||||
config[section_name] = schema.convert(items)
|
||||
except exceptions.ConfigError as error:
|
||||
errors[section_name] = error
|
||||
|
||||
if errors:
|
||||
for section_name, error in errors.items():
|
||||
logger.error('[%s] config errors:', section_name)
|
||||
for key in error:
|
||||
logger.error('%s %s', key, error[key])
|
||||
sys.exit(1)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def create_file_structures():
|
||||
path.get_or_create_dir('$XDG_DATA_DIR/mopidy')
|
||||
path.get_or_create_file('$XDG_CONFIG_DIR/mopidy/mopidy.conf')
|
||||
|
||||
@ -1,7 +1,16 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import codecs
|
||||
import ConfigParser as configparser
|
||||
import io
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from mopidy.config.schemas import *
|
||||
from mopidy.config.values import *
|
||||
from mopidy.utils import path
|
||||
|
||||
logger = logging.getLogger('mopdiy.config')
|
||||
|
||||
|
||||
default_config = """
|
||||
@ -44,3 +53,84 @@ config_schemas['proxy']['password'] = String(optional=True, secret=True)
|
||||
|
||||
# NOTE: if multiple outputs ever comes something like LogLevelConfigSchema
|
||||
#config_schemas['audio.outputs'] = config.AudioOutputConfigSchema()
|
||||
|
||||
|
||||
# TODO: update API to load(files, defaults, overrides) this should not need to
|
||||
# know about extensions
|
||||
def load(files, overrides, extensions=None):
|
||||
parser = configparser.RawConfigParser()
|
||||
|
||||
files = [path.expand_path(f) for f in files]
|
||||
sources = ['builtin-defaults'] + files + ['command-line']
|
||||
logger.info('Loading config from: %s', ', '.join(sources))
|
||||
|
||||
# Read default core config
|
||||
parser.readfp(io.StringIO(default_config))
|
||||
|
||||
# Read default extension config
|
||||
for extension in extensions or []:
|
||||
parser.readfp(io.StringIO(extension.get_default_config()))
|
||||
|
||||
# Load config from a series of config files
|
||||
for filename in files:
|
||||
# TODO: if this is the initial load of logging config we might not have
|
||||
# a logger at this point, we might want to handle this better.
|
||||
try:
|
||||
filehandle = codecs.open(filename, encoding='utf-8')
|
||||
parser.readfp(filehandle)
|
||||
except IOError:
|
||||
logger.debug('Config file %s not found; skipping', filename)
|
||||
continue
|
||||
except UnicodeDecodeError:
|
||||
logger.error('Config file %s is not UTF-8 encoded', filename)
|
||||
sys.exit(1)
|
||||
|
||||
raw_config = {}
|
||||
for section in parser.sections():
|
||||
raw_config[section] = dict(parser.items(section))
|
||||
|
||||
# TODO: move out of file loading code?
|
||||
for section, key, value in overrides or []:
|
||||
raw_config.setdefault(section, {})[key] = value
|
||||
|
||||
return raw_config
|
||||
|
||||
|
||||
# TODO: switch API to validate(raw_config, schemas) this should not need to
|
||||
# know about extensions
|
||||
def validate(raw_config, schemas, extensions=None):
|
||||
# Collect config schemas to validate against
|
||||
sections_and_schemas = schemas.items()
|
||||
for extension in extensions or []:
|
||||
sections_and_schemas.append(
|
||||
(extension.ext_name, extension.get_config_schema()))
|
||||
|
||||
# Get validated config
|
||||
config = {}
|
||||
errors = {}
|
||||
for section_name, schema in sections_and_schemas:
|
||||
if section_name not in raw_config:
|
||||
errors[section_name] = {section_name: 'section not found'}
|
||||
try:
|
||||
items = raw_config[section_name].items()
|
||||
config[section_name] = schema.convert(items)
|
||||
except exceptions.ConfigError as error:
|
||||
errors[section_name] = error
|
||||
|
||||
if errors:
|
||||
# TODO: raise error instead.
|
||||
#raise exceptions.ConfigError(errors)
|
||||
for section_name, error in errors.items():
|
||||
logger.error('[%s] config errors:', section_name)
|
||||
for key in error:
|
||||
logger.error('%s %s', key, error[key])
|
||||
sys.exit(1)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def parse_override(override):
|
||||
"""Parse section/key=value override."""
|
||||
section, remainder = override.split('/', 1)
|
||||
key, value = remainder.split('=', 1)
|
||||
return (section, key, value)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user