Merge CliError into UserError

This commit is contained in:
Markus Unterwaditzer 2016-02-10 15:09:25 +01:00
parent 9e8b5f2dad
commit be4baba19e
6 changed files with 56 additions and 50 deletions

View file

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

View file

@ -24,21 +24,6 @@ class AppContext(object):
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):
@functools.wraps(f)
def inner(*a, **kw):

View file

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

View file

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

View file

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