From dc5500892b1083a9c5ab7f0d0d2198a9a290db6e Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Mon, 19 Sep 2016 15:26:01 +0200 Subject: [PATCH] More config refactor --- tests/cli/test_config.py | 15 +++--- vdirsyncer/cli/config.py | 113 +++++++++++++++++++-------------------- 2 files changed, 61 insertions(+), 67 deletions(-) diff --git a/tests/cli/test_config.py b/tests/cli/test_config.py index a17d3e4..8f79a95 100644 --- a/tests/cli/test_config.py +++ b/tests/cli/test_config.py @@ -61,10 +61,11 @@ def test_read_config(read_config): ''') assert c.general == {'status_path': '/tmp/status/'} - assert c.pairs == { - 'bob': ('bob_a', 'bob_b', - {'collections': None, 'bam': True, 'foo': 'bar'}) - } + + assert set(c.pairs) == {'bob'} + bob = c.pairs['bob'] + assert bob.options == {'bam': True, 'foo': 'bar', 'collections': None} + assert c.storages == { 'bob_a': {'type': 'filesystem', 'path': '/tmp/contacts/', 'fileext': '.vcf', 'yesno': False, 'number': 42, @@ -236,10 +237,8 @@ def test_parse_config_value(parse_config_value): assert x('""') == ('', 0) -def test_validate_pair_section_collections_param(): - def x(val): - return cli.config._validate_pair_section({'collections': val}) - +def test_validate_collections_param(): + x = cli.config._validate_collections_param x(None) x(["c", "a", "b"]) pytest.raises(ValueError, x, [None]) diff --git a/vdirsyncer/cli/config.py b/vdirsyncer/cli/config.py index ae09d95..a33fd2b 100644 --- a/vdirsyncer/cli/config.py +++ b/vdirsyncer/cli/config.py @@ -6,7 +6,7 @@ from itertools import chain from . import cli_logger from .fetchparams import expand_fetch_params from .. import PROJECT_HOME, exceptions -from ..utils import cached_property, expand_path +from ..utils import expand_path try: from ConfigParser import RawConfigParser @@ -48,15 +48,7 @@ def _validate_general_section(general_config): .format(PROJECT_HOME), problems=problems) -def _validate_pair_section(pair_config): - try: - collections = pair_config['collections'] - except KeyError: - raise ValueError('collections parameter missing.\n\n' - 'As of 0.9.0 this parameter has no default anymore. ' - 'Set `collections = null` explicitly in your pair ' - 'config.') - +def _validate_collections_param(collections): if collections is None: return @@ -96,7 +88,7 @@ def _validate_pair_section(pair_config): .format(i=i, e=str(e))) -class ConfigReader: +class _ConfigReader: def __init__(self, f): self._file = f self._parser = c = RawConfigParser() @@ -107,42 +99,23 @@ class ConfigReader: self._pairs = {} self._storages = {} - self._handlers = { - 'general': self._handle_general, - 'pair': self._handle_pair, - 'storage': self._handle_storage - } - - def _get_options(self, s): - return dict(parse_options(self._parser.items(s), section=s)) - - def _handle_storage(self, storage_name, options): - options['instance_name'] = storage_name - self._storages[storage_name] = options - - def _handle_pair(self, pair_name, options): - _validate_pair_section(options) - a, b = options.pop('a'), options.pop('b') - self._pairs[pair_name] = a, b, options - - def _handle_general(self, _, options): - if self._general: - raise ValueError('More than one general section.') - self._general = options - def _parse_section(self, section_type, name, options): validate_section_name(name, section_type) if name in self._seen_names: raise ValueError('Name "{}" already used.'.format(name)) self._seen_names.add(name) - try: - f = self._handlers[section_type] - except KeyError: + if section_type == 'general': + if self._general: + raise ValueError('More than one general section.') + self._general = options + elif section_type == 'storage': + self._storages[name] = options + elif section_type == 'pair': + self._pairs[name] = options + else: raise ValueError('Unknown section type.') - f(name, options) - def parse(self): for section in self._parser.sections(): if ' ' in section: @@ -151,8 +124,11 @@ class ConfigReader: section_type = name = section try: - self._parse_section(section_type, name, - self._get_options(section)) + self._parse_section( + section_type, name, + dict(parse_options(self._parser.items(section), + section=section)) + ) except ValueError as e: raise exceptions.UserError( 'Section "{}": {}'.format(section, str(e))) @@ -212,12 +188,20 @@ def parse_options(items, section=None): class Config(object): def __init__(self, general, pairs, storages): self.general = general - self.pairs = pairs self.storages = storages + for name, options in storages.items(): + options['instance_name'] = name + + self.pairs = {} + for name, options in pairs.items(): + try: + self.pairs[name] = PairConfig(self, name, options) + except ValueError as e: + raise exceptions.UserError('Pair {}: {}'.format(name, e)) @classmethod def from_fileobject(cls, f): - reader = ConfigReader(f) + reader = _ConfigReader(f) return cls(*reader.parse()) @classmethod @@ -240,41 +224,52 @@ class Config(object): .format(fname, e) ) - def get_storage_args(self, storage_name, pair_name=None): + def get_storage_args(self, storage_name): try: args = self.storages[storage_name] except KeyError: - pair_pref = 'Pair {}: '.format(pair_name) if pair_name else '' raise exceptions.UserError( - '{}Storage {!r} not found. ' + 'Storage {!r} not found. ' 'These are the configured storages: {}' - .format(pair_pref, storage_name, list(self.storages)) + .format(storage_name, list(self.storages)) ) else: return expand_fetch_params(args) def get_pair(self, pair_name): try: - return PairConfig(self, pair_name, *self.pairs[pair_name]) + return self.pairs[pair_name] except KeyError as e: raise exceptions.PairNotFound(e, pair_name=pair_name) class PairConfig(object): - def __init__(self, config, name, name_a, name_b, pair_options): - self._config = config + def __init__(self, full_config, name, options): + self._config = full_config self.name = name - self.name_a = name_a - self.name_b = name_b - self.options = pair_options + self.name_a = options.pop('a') + self.name_b = options.pop('b') + self.options = options - @cached_property - def config_a(self): - return self._config.get_storage_args(self.name_a, pair_name=self.name) + conflict_resolution = self.options.get('conflict_resolution', None) + if conflict_resolution in (None, 'a wins', 'b wins'): + self.conflict_resolution = conflict_resolution + else: + raise ValueError('Invalid value for `conflict_resolution`.') - @cached_property - def config_b(self): - return self._config.get_storage_args(self.name_b, pair_name=self.name) + try: + collections = self.options['collections'] + except KeyError: + raise ValueError( + 'collections parameter missing.\n\n' + 'As of 0.9.0 this parameter has no default anymore. ' + 'Set `collections = null` explicitly in your pair config.' + ) + else: + _validate_collections_param(collections) + + self.config_a = self._config.get_storage_args(self.name_a) + self.config_b = self._config.get_storage_args(self.name_b) class CollectionConfig(object):