mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-04-27 14:57:41 +00:00
Merge pull request #330 from untitaker/kill-clierror
Merge CliError into UserError
This commit is contained in:
commit
3c9ef726df
6 changed files with 56 additions and 50 deletions
|
|
@ -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 (
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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'''
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue