Merge branch 'release/v0.19.x' into develop

Conflicts:
	mopidy/config/__init__.py
This commit is contained in:
Stein Magnus Jodal 2014-08-12 23:42:59 +02:00
commit ae3a932ed3
9 changed files with 63 additions and 25 deletions

View File

@ -26,6 +26,10 @@ Bug fix release.
- Logging: Fix that some loggers would be disabled if
:confval:`logging/config_file` was set. (Fixes: :issue:`740`)
- Core: Return exit status 1 when exiting because of initialization error.
- Configuration: :option:`mopidy --config` now supports directories.
v0.19.3 (2014-08-03)
====================

View File

@ -50,11 +50,12 @@ Options
Save debug log to the file specified in the :confval:`logging/debug_file`
config value, typically ``./mopidy.log``.
.. cmdoption:: --config <file>
.. cmdoption:: --config <file|directory>
Specify config file to use. To use multiple config files, separate them
with a colon. The later files override the earlier ones if there's a
conflict.
Specify config files and directories to use. To use multiple config files
or directories, separate them with a colon. The later files override the
earlier ones if there's a conflict. When specifying a directory, all files
ending in .conf in the directory are used.
.. cmdoption:: --option <option>, -o <option>

View File

@ -276,8 +276,10 @@ class RootCommand(Command):
exceptions.FrontendError,
exceptions.MixerError):
logger.info('Initialization error. Exiting...')
exit_status_code = 1
except KeyboardInterrupt:
logger.info('Interrupted. Exiting...')
exit_status_code = 0
finally:
loop.quit()
self.stop_frontends(frontend_classes)
@ -286,6 +288,7 @@ class RootCommand(Command):
self.stop_audio()
self.stop_mixer(mixer_class)
process.stop_remaining_actors()
return exit_status_code
def get_mixer_class(self, config, mixer_classes):
logger.debug(

View File

@ -114,27 +114,14 @@ def _load(files, defaults, overrides):
# Load config from a series of config files
files = [path.expand_path(f) for f in files]
for filename in files:
if not os.path.exists(filename):
logger.debug(
'Loading config from %s failed; it does not exist', filename)
continue
try:
logger.info('Loading config from %s', filename)
with io.open(filename, 'rb') as filehandle:
parser.readfp(filehandle)
except configparser.MissingSectionHeaderError as e:
logger.warning('%s does not have a config section, not loaded.',
filename)
except configparser.ParsingError as e:
linenos = ', '.join(str(lineno) for lineno, line in e.errors)
logger.warning(
'%s has errors, line %s has been ignored.', filename, linenos)
except IOError:
# 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.
logger.debug('Config file %s not found; skipping', filename)
for name in files:
if os.path.isdir(name):
for filename in os.listdir(name):
filename = os.path.join(name, filename)
if os.path.isfile(filename) and filename.endswith('.conf'):
_load_file(parser, filename)
else:
_load_file(parser, name)
# If there have been parse errors there is a python bug that causes the
# values to be lists, this little trick coerces these into strings.
@ -151,6 +138,29 @@ def _load(files, defaults, overrides):
return raw_config
def _load_file(parser, filename):
if not os.path.exists(filename):
logger.debug(
'Loading config from %s failed; it does not exist', filename)
return
try:
logger.info('Loading config from %s', filename)
with io.open(filename, 'rb') as filehandle:
parser.readfp(filehandle)
except configparser.MissingSectionHeaderError as e:
logger.warning('%s does not have a config section, not loaded.',
filename)
except configparser.ParsingError as e:
linenos = ', '.join(str(lineno) for lineno, line in e.errors)
logger.warning(
'%s has errors, line %s has been ignored.', filename, linenos)
except IOError:
# 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.
logger.debug('Config file %s not found; skipping', filename)
def _validate(raw_config, schemas):
# Get validated config
config = {}

View File

@ -60,6 +60,18 @@ class LoadConfigTest(unittest.TestCase):
result = config._load([file1, file2], [], [])
self.assertEqual(expected, result)
def test_load_directory(self):
directory = path_to_data_dir('conf1.d')
expected = {'foo': {'bar': 'baz'}, 'foo2': {'bar': 'baz'}}
result = config._load([directory], [], [])
self.assertEqual(expected, result)
def test_load_directory_only_conf_files(self):
directory = path_to_data_dir('conf2.d')
expected = {'foo': {'bar': 'baz'}}
result = config._load([directory], [], [])
self.assertEqual(expected, result)
def test_load_file_with_utf8(self):
expected = {'foo': {'bar': 'æøå'.encode('utf-8')}}
result = config._load([path_to_data_dir('file3.conf')], [], [])

View File

@ -0,0 +1,2 @@
[foo]
bar = baz

View File

@ -0,0 +1,2 @@
[foo2]
bar = baz

View File

@ -0,0 +1,2 @@
[foo]
bar = baz

View File

@ -0,0 +1,2 @@
[foo2]
bar = baz