diff --git a/mopidy/config/__init__.py b/mopidy/config/__init__.py index 9d33bcb6..85e0fa61 100644 --- a/mopidy/config/__init__.py +++ b/mopidy/config/__init__.py @@ -105,10 +105,11 @@ def _validate(raw_config, schemas): errors = {} for schema in schemas: values = raw_config.get(schema.name, {}) - result, error = schema.convert(values) + result, error = schema.deserialize(values) if error: errors[schema.name] = error - config[schema.name] = result + if result: + config[schema.name] = result return config, errors diff --git a/mopidy/config/schemas.py b/mopidy/config/schemas.py index 72c0ee6d..90d966d3 100644 --- a/mopidy/config/schemas.py +++ b/mopidy/config/schemas.py @@ -40,9 +40,11 @@ class ConfigSchema(object): """Logical group of config values that correspond to a config section. Schemas are set up by assigning config keys with config values to - instances. Once setup :meth:`convert` can be called with a list of - ``(key, value)`` tuples to process. For convienience we also support - :meth:`format` method that can used for printing out the converted values. + instances. Once setup :meth:`deserialize` can be called with a dict of + values to process. For convienience we also support :meth:`format` method + that can used for converting the values to a dict that can be printed and + :meth:`serialize` for converting the values to a form suitable for + persistence. """ # TODO: Use collections.OrderedDict once 2.6 support is gone (#344) def __init__(self, name): @@ -58,20 +60,7 @@ class ConfigSchema(object): def __getitem__(self, key): return self._schema[key] - def format(self, values): - """Returns the schema as a config section with the given ``values`` - filled in""" - # TODO: should the output be encoded utf-8 since we use that in - # serialize for strings? - lines = ['[%s]' % self.name] - for key in self._order: - value = values.get(key) - if value is not None: - lines.append('%s = %s' % ( - key, self._schema[key].format(value))) - return '\n'.join(lines) - - def convert(self, values): + def deserialize(self, values): """Validates the given ``values`` using the config schema. Returns a tuple with cleaned values and errors.""" @@ -95,6 +84,23 @@ class ConfigSchema(object): return result, errors + def serialize(self, values): + pass + + def format(self, values): + """Returns the schema as a config section with the given ``values`` + filled in""" + # TODO: should the output be encoded utf-8 since we use that in + # serialize for strings? + lines = ['[%s]' % self.name] + for key in self._order: + value = values.get(key) + if value is not None: + lines.append('%s = %s' % ( + key, self._schema[key].format(value))) + return '\n'.join(lines) + + class ExtensionConfigSchema(ConfigSchema): """Sub-classed :class:`ConfigSchema` for use in extensions. @@ -105,7 +111,7 @@ class ExtensionConfigSchema(ConfigSchema): super(ExtensionConfigSchema, self).__init__(name) self['enabled'] = types.Boolean() - # TODO: override convert to gate on enabled=true? + # TODO: override serialize to gate on enabled=true? class LogLevelConfigSchema(object): @@ -119,15 +125,7 @@ class LogLevelConfigSchema(object): self.name = name self._config_value = types.LogLevel() - def format(self, values): - lines = ['[%s]' % self.name] - for key, value in sorted(values.items()): - if value is not None: - lines.append('%s = %s' % ( - key, self._config_value.format(value))) - return '\n'.join(lines) - - def convert(self, values): + def deserialize(self, values): errors = {} result = {} @@ -137,3 +135,14 @@ class LogLevelConfigSchema(object): except ValueError as e: # deserialization failed errors[key] = str(e) return result, errors + + def serialize(self, values): + pass + + def format(self, values): + lines = ['[%s]' % self.name] + for key, value in sorted(values.items()): + if value is not None: + lines.append('%s = %s' % ( + key, self._config_value.format(value))) + return '\n'.join(lines) diff --git a/tests/config/config_test.py b/tests/config/config_test.py index c90486c7..b112de1f 100644 --- a/tests/config/config_test.py +++ b/tests/config/config_test.py @@ -53,38 +53,38 @@ class LoadConfigTest(unittest.TestCase): class ValidateTest(unittest.TestCase): def setUp(self): - self.schema = mock.Mock() - self.schema.name = 'foo' + self.schema = config.ConfigSchema('foo') + self.schema['bar'] = config.ConfigValue() def test_empty_config_no_schemas(self): conf, errors = config._validate({}, []) self.assertEqual({}, conf) - self.assertEqual([], errors) + self.assertEqual({}, errors) def test_config_no_schemas(self): raw_config = {'foo': {'bar': 'baz'}} conf, errors = config._validate(raw_config, []) self.assertEqual({}, conf) - self.assertEqual([], errors) + self.assertEqual({}, errors) def test_empty_config_single_schema(self): conf, errors = config._validate({}, [self.schema]) self.assertEqual({}, conf) - self.assertEqual(['foo: section not found.'], errors) + self.assertEqual({'foo': {'bar': 'config key not found.'}}, errors) def test_config_single_schema(self): raw_config = {'foo': {'bar': 'baz'}} - self.schema.convert.return_value = {'baz': 'bar'} conf, errors = config._validate(raw_config, [self.schema]) - self.assertEqual({'foo': {'baz': 'bar'}}, conf) - self.assertEqual([], errors) + self.assertEqual({'foo': {'bar': 'baz'}}, conf) + self.assertEqual({}, errors) def test_config_single_schema_config_error(self): raw_config = {'foo': {'bar': 'baz'}} - self.schema.convert.side_effect = exceptions.ConfigError({'bar': 'bad'}) + self.schema['bar'] = mock.Mock() + self.schema['bar'].deserialize.side_effect = ValueError('bad') conf, errors = config._validate(raw_config, [self.schema]) - self.assertEqual(['foo/bar: bad'], errors) self.assertEqual({}, conf) + self.assertEqual({'foo': {'bar': 'bad'}}, errors) # TODO: add more tests diff --git a/tests/config/schemas_test.py b/tests/config/schemas_test.py index 80f06f39..f8d908f7 100644 --- a/tests/config/schemas_test.py +++ b/tests/config/schemas_test.py @@ -35,45 +35,44 @@ class ConfigSchemaTest(unittest.TestCase): result = self.schema.format(self.values) self.assertNotIn('unknown = rty', result) - def test_convert(self): - self.schema.convert(self.values) + def test_deserialize(self): + self.schema.deserialize(self.values) - def test_convert_with_missing_value(self): + def test_deserialize_with_missing_value(self): del self.values['foo'] - result, errors = self.schema.convert(self.values) + result, errors = self.schema.deserialize(self.values) self.assertIn('not found', errors['foo']) self.assertItemsEqual(['bar', 'baz'], result.keys()) - def test_convert_with_extra_value(self): + def test_deserialize_with_extra_value(self): self.values['extra'] = '123' - result, errors = self.schema.convert(self.values) + result, errors = self.schema.deserialize(self.values) self.assertIn('unknown', errors['extra']) self.assertItemsEqual(['foo', 'bar', 'baz'], result.keys()) - def test_convert_with_deserialization_error(self): + def test_deserialize_with_deserialization_error(self): self.schema['foo'].deserialize.side_effect = ValueError('failure') - - result, errors = self.schema.convert(self.values) + result, errors = self.schema.deserialize(self.values) self.assertIn('failure', errors['foo']) self.assertItemsEqual(['bar', 'baz'], result.keys()) - def test_convert_with_multiple_deserialization_errors(self): + def test_deserialize_with_multiple_deserialization_errors(self): self.schema['foo'].deserialize.side_effect = ValueError('failure') self.schema['bar'].deserialize.side_effect = ValueError('other') - result, errors = self.schema.convert(self.values) + result, errors = self.schema.deserialize(self.values) self.assertIn('failure', errors['foo']) self.assertIn('other', errors['bar']) self.assertItemsEqual(['baz'], result.keys()) - def test_convert_deserialization_unknown_and_missing_errors(self): + def test_deserialize_deserialization_unknown_and_missing_errors(self): self.values['extra'] = '123' self.schema['bar'].deserialize.side_effect = ValueError('failure') del self.values['baz'] - result, errors = self.schema.convert(self.values) + result, errors = self.schema.deserialize(self.values) self.assertIn('unknown', errors['extra']) self.assertNotIn('foo', errors) self.assertIn('failure', errors['bar']) @@ -90,7 +89,7 @@ class ExtensionConfigSchemaTest(unittest.TestCase): class LogLevelConfigSchemaTest(unittest.TestCase): def test_conversion(self): schema = schemas.LogLevelConfigSchema('test') - result, errors = schema.convert({'foo.bar': 'DEBUG', 'baz': 'INFO'}) + result, errors = schema.deserialize({'foo.bar': 'DEBUG', 'baz': 'INFO'}) self.assertEqual(logging.DEBUG, result['foo.bar']) self.assertEqual(logging.INFO, result['baz'])