Merge pull request #330 from untitaker/kill-clierror

Merge CliError into UserError
This commit is contained in:
Markus Unterwaditzer 2016-02-10 16:03:27 +01:00
commit 3c9ef726df
6 changed files with 56 additions and 50 deletions

View file

@ -4,7 +4,7 @@ from textwrap import dedent
import pytest import pytest
import vdirsyncer.cli.utils # noqa import vdirsyncer.cli.utils # noqa
from vdirsyncer import cli from vdirsyncer import cli, exceptions
from vdirsyncer.cli.config import parse_config_value, \ from vdirsyncer.cli.config import parse_config_value, \
read_config as _read_config read_config as _read_config
@ -70,7 +70,7 @@ def test_storage_instance_from_config(monkeypatch):
def test_missing_general_section(read_config): def test_missing_general_section(read_config):
with pytest.raises(cli.CliError) as excinfo: with pytest.raises(exceptions.UserError) as excinfo:
read_config(u''' read_config(u'''
[pair my_pair] [pair my_pair]
a = my_a a = my_a
@ -87,17 +87,17 @@ def test_missing_general_section(read_config):
fileext = .txt fileext = .txt
''') ''')
assert 'Invalid general section.' in excinfo.value.msg assert 'Invalid general section.' in str(excinfo.value)
def test_wrong_general_section(read_config): def test_wrong_general_section(read_config):
with pytest.raises(cli.CliError) as excinfo: with pytest.raises(exceptions.UserError) as excinfo:
read_config(u''' read_config(u'''
[general] [general]
wrong = true wrong = true
''') ''')
assert 'Invalid general section.' in excinfo.value.msg assert 'Invalid general section.' in str(excinfo.value)
assert excinfo.value.problems == [ assert excinfo.value.problems == [
'general section doesn\'t take the parameters: wrong', 'general section doesn\'t take the parameters: wrong',
'general section is missing the parameters: status_path' 'general section is missing the parameters: status_path'
@ -112,7 +112,7 @@ def test_invalid_storage_name():
[storage foo.bar] [storage foo.bar]
''')) '''))
with pytest.raises(cli.CliError) as excinfo: with pytest.raises(exceptions.UserError) as excinfo:
_read_config(f) _read_config(f)
assert 'invalid characters' in str(excinfo.value).lower() assert 'invalid characters' in str(excinfo.value).lower()
@ -169,7 +169,7 @@ def test_invalid_collections_arg():
fileext = .txt fileext = .txt
''')) '''))
with pytest.raises(cli.utils.CliError) as excinfo: with pytest.raises(exceptions.UserError) as excinfo:
_read_config(f) _read_config(f)
assert ( assert (

View file

@ -24,21 +24,6 @@ class AppContext(object):
pass_context = click.make_pass_decorator(AppContext, ensure=True) pass_context = click.make_pass_decorator(AppContext, ensure=True)
class CliError(RuntimeError):
def __init__(self, msg, problems=None):
self.msg = msg
self.problems = problems
RuntimeError.__init__(self, msg)
def __str__(self):
msg = self.msg
li = u'\n - '
for problem in self.problems or ():
msg += u'{}{}'.format(li, problem)
return msg
def catch_errors(f): def catch_errors(f):
@functools.wraps(f) @functools.wraps(f)
def inner(*a, **kw): def inner(*a, **kw):

View file

@ -3,9 +3,9 @@ import os
import string import string
from itertools import chain from itertools import chain
from . import CliError, cli_logger from . import cli_logger
from .fetchparams import expand_fetch_params from .fetchparams import expand_fetch_params
from .. import PROJECT_HOME from .. import PROJECT_HOME, exceptions
from ..utils import cached_property, expand_path from ..utils import cached_property, expand_path
from ..utils.compat import text_type from ..utils.compat import text_type
@ -23,10 +23,10 @@ def validate_section_name(name, section_type):
invalid = set(name) - SECTION_NAME_CHARS invalid = set(name) - SECTION_NAME_CHARS
if invalid: if invalid:
chars_display = ''.join(sorted(SECTION_NAME_CHARS)) chars_display = ''.join(sorted(SECTION_NAME_CHARS))
raise CliError('The {}-section "{}" contains invalid characters. Only ' raise exceptions.UserError(
'the following characters are allowed for storage and ' 'The {}-section "{}" contains invalid characters. Only '
'pair names:\n{}'.format(section_type, name, 'the following characters are allowed for storage and '
chars_display)) 'pair names:\n{}'.format(section_type, name, chars_display))
def _validate_general_section(general_config): def _validate_general_section(general_config):
@ -48,9 +48,10 @@ def _validate_general_section(general_config):
.format(u', '.join(missing))) .format(u', '.join(missing)))
if problems: if problems:
raise CliError(u'Invalid general section. Copy the example ' raise exceptions.UserError(
u'config from the repository and edit it: {}' u'Invalid general section. Copy the example '
.format(PROJECT_HOME), problems=problems) u'config from the repository and edit it: {}'
.format(PROJECT_HOME), problems=problems)
def _validate_pair_section(pair_config): def _validate_pair_section(pair_config):
@ -77,8 +78,10 @@ def load_config():
with open(fname) as f: with open(fname) as f:
general, pairs, storages = read_config(f) general, pairs, storages = read_config(f)
except Exception as e: except Exception as e:
raise CliError('Error during reading config {}: {}' raise exceptions.UserError(
.format(fname, e)) 'Error during reading config {}: {}'
.format(fname, e)
)
return Config(general, pairs, storages) return Config(general, pairs, storages)
@ -105,7 +108,8 @@ def read_config(f):
def handle_general(_, options): def handle_general(_, options):
if general: if general:
raise CliError('More than one general section in config file.') raise exceptions.UserError(
'More than one general section in config file.')
general.update(options) general.update(options)
def bad_section(name, options): def bad_section(name, options):
@ -125,7 +129,8 @@ def read_config(f):
f = handlers.get(section_type, bad_section) f = handlers.get(section_type, bad_section)
f(name, get_options(section)) f(name, get_options(section))
except ValueError as e: except ValueError as e:
raise CliError('Section `{}`: {}'.format(section, str(e))) raise exceptions.UserError(
'Section `{}`: {}'.format(section, str(e)))
_validate_general_section(general) _validate_general_section(general)
if getattr(f, 'name', None): if getattr(f, 'name', None):
@ -189,7 +194,7 @@ class Config(object):
args = self.storages[storage_name] args = self.storages[storage_name]
except KeyError: except KeyError:
pair_pref = 'Pair {}: '.format(pair_name) if pair_name else '' pair_pref = 'Pair {}: '.format(pair_name) if pair_name else ''
raise CliError( raise exceptions.UserError(
'{}Storage {!r} not found. ' '{}Storage {!r} not found. '
'These are the configured storages: {}' 'These are the configured storages: {}'
.format(pair_pref, storage_name, list(self.storages)) .format(pair_pref, storage_name, list(self.storages))

View file

@ -4,10 +4,11 @@ import functools
import json import json
from .config import CollectionConfig from .config import CollectionConfig
from .utils import CliError, JobFailed, cli_logger, coerce_native, \ from .utils import JobFailed, cli_logger, coerce_native, \
collections_for_pair, get_status_name, handle_cli_error, load_status, \ collections_for_pair, get_status_name, handle_cli_error, load_status, \
save_status, storage_class_from_config, storage_instance_from_config save_status, storage_class_from_config, storage_instance_from_config
from .. import exceptions
from ..sync import sync from ..sync import sync
from ..utils.compat import to_unicode from ..utils.compat import to_unicode
@ -28,7 +29,7 @@ def prepare_pair(wq, pair_name, collections, config, callback, **kwargs):
try: try:
config_a, config_b = all_collections[collection_name] config_a, config_b = all_collections[collection_name]
except KeyError: except KeyError:
raise CliError( raise exceptions.UserError(
'Pair {}: Collection {} not found. These are the ' 'Pair {}: Collection {} not found. These are the '
'configured collections:\n{}' 'configured collections:\n{}'
.format(pair_name, .format(pair_name,
@ -98,8 +99,10 @@ def repair_collection(config, collection):
if config['collection'] == collection: if config['collection'] == collection:
break break
else: else:
raise CliError('Couldn\'t find collection {} for storage {}.' raise exceptions.UserError(
.format(collection, storage_name)) 'Couldn\'t find collection {} for storage {}.'
.format(collection, storage_name)
)
config['type'] = storage_type config['type'] = storage_type
storage = storage_instance_from_config(config) storage = storage_instance_from_config(config)

View file

@ -14,7 +14,7 @@ import click
import click_threading import click_threading
from . import CliError, cli_logger from . import cli_logger
from .. import DOCS_HOME, exceptions from .. import DOCS_HOME, exceptions
from ..sync import IdentConflict, StorageEmpty, SyncConflict from ..sync import IdentConflict, StorageEmpty, SyncConflict
from ..utils import expand_path, get_class_init_args from ..utils import expand_path, get_class_init_args
@ -78,7 +78,7 @@ def handle_cli_error(status_name=None):
try: try:
raise raise
except (CliError, exceptions.UserError) as e: except exceptions.UserError as e:
cli_logger.critical(e) cli_logger.critical(e)
except StorageEmpty as e: except StorageEmpty as e:
cli_logger.error( cli_logger.error(
@ -243,10 +243,11 @@ def _handle_collection_not_found(config, collection, e=None):
except NotImplementedError as e: except NotImplementedError as e:
cli_logger.error(e) cli_logger.error(e)
raise CliError('Unable to find or create collection "{collection}" for ' raise exceptions.UserError(
'storage "{storage}". Please create the collection ' 'Unable to find or create collection "{collection}" for '
'yourself.'.format(collection=collection, 'storage "{storage}". Please create the collection '
storage=storage_name)) 'yourself.'.format(collection=collection,
storage=storage_name))
def _collections_for_pair_impl(status_path, pair): def _collections_for_pair_impl(status_path, pair):
@ -341,7 +342,8 @@ def storage_class_from_config(config):
try: try:
cls = storage_names[storage_name] cls = storage_names[storage_name]
except KeyError: except KeyError:
raise CliError('Unknown storage type: {}'.format(storage_name)) raise exceptions.UserError(
'Unknown storage type: {}'.format(storage_name))
return cls, config return cls, config
@ -368,7 +370,7 @@ def storage_instance_from_config(config, create=True):
def handle_storage_init_error(cls, config): def handle_storage_init_error(cls, config):
e = sys.exc_info()[1] e = sys.exc_info()[1]
if isinstance(e, (click.Abort, CliError, KeyboardInterrupt)): if isinstance(e, (click.Abort, exceptions.UserError, KeyboardInterrupt)):
raise raise
all, required = get_class_init_args(cls) all, required = get_class_init_args(cls)
@ -393,8 +395,10 @@ def handle_storage_init_error(cls, config):
cli_logger.exception('') cli_logger.exception('')
problems.append(str(e)) problems.append(str(e))
raise CliError(u'Failed to initialize {}'.format(config['instance_name']), raise exceptions.UserError(
problems=problems) u'Failed to initialize {}'.format(config['instance_name']),
problems=problems
)
class WorkerQueue(object): class WorkerQueue(object):

View file

@ -21,6 +21,15 @@ class UserError(Error, ValueError):
'''Wrapper exception to be used to signify the traceback should not be '''Wrapper exception to be used to signify the traceback should not be
shown to the user.''' shown to the user.'''
problems = None
def __str__(self):
msg = Error.__str__(self)
for problem in self.problems or ():
msg += u'\n - {}'.format(problem)
return msg
class CollectionNotFound(Error): class CollectionNotFound(Error):
'''Collection not found''' '''Collection not found'''