This commit is contained in:
Markus Unterwaditzer 2014-05-23 15:14:38 +02:00
parent dce438d758
commit 1f85b4db6f
2 changed files with 36 additions and 21 deletions

View file

@ -14,7 +14,7 @@ import sys
import argvard
from .storage import storage_names
from .sync import sync, StorageEmpty
from .sync import sync, StorageEmpty, SyncConflict
from .utils import expand_path, parse_options, split_dict, get_class_init_args
import vdirsyncer.log as log
@ -29,6 +29,7 @@ except ImportError:
cli_logger = log.get(__name__)
PROJECT_HOME = 'https://github.com/untitaker/vdirsyncer'
DOCS_HOME = 'https://vdirsyncer.readthedocs.org/en/latest'
class CliError(RuntimeError):
@ -296,16 +297,30 @@ def sync_collection(config_a, config_b, pair_name, collection, pair_options,
force_delete=status_name in force_delete
)
except StorageEmpty as e:
raise CliError(
'{collection_description}: Storage "{side}" ({storage}) was '
'completely emptied. Use "--force-delete {status_name}" to '
'synchronize that emptyness to the other side, or delete the '
'status by yourself to restore the items from the non-empty '
'side.'.format(
collection_description=collection_description,
cli_logger.critical(
'{collection}: Storage "{side}" ({storage}) was completely '
'emptied. Use "--force-delete {status_name}" to synchronize that '
'emptyness to the other side, or delete the status by yourself to '
'restore the items from the non-empty side.'.format(
collection=collection_description,
side='a' if e.empty_storage is a else 'b',
storage=e.empty_storage,
status_name=status_name
)
)
except SyncConflict as e:
cli_logger.critical(
'{collection}: One item changed on both sides. Resolve this '
'conflict manually, or by setting the `conflict_resolution` '
'parameter in your config file.\n'
'See also {docs}/api.html#pair-section\n'
'Item ID: {e.ident}\n'
'Item href on side A: {e.href_a}\n'
'Item href on side B: {e.href_b}\n'
.format(collection=collection_description, e=e, docs=DOCS_HOME)
)
except Exception:
cli_logger.exception('Unhandled exception occured while syncing {}.'
.format(collection_description))
save_status(general['status_path'], status_name, status)

View file

@ -25,6 +25,10 @@ sync_logger = log.get(__name__)
class SyncError(exceptions.Error):
'''Errors related to synchronization.'''
def __init__(self, *args, **kwargs):
self.__dict__.update(kwargs)
super(SyncError, self).__init__(*args)
class SyncConflict(SyncError):
'''
@ -39,10 +43,6 @@ class StorageEmpty(SyncError):
The first argument is the empty storage.
'''
@property
def empty_storage(self):
return self.args[0]
def prepare_list(storage, href_to_status):
rv = {}
@ -63,7 +63,7 @@ def prepare_list(storage, href_to_status):
props['item'] = item
props['ident'] = item.ident
if props['etag'] != etag:
raise SyncConflict('Etag changed during sync.')
raise SyncError('Etag changed during sync.')
return rv
@ -101,7 +101,7 @@ def sync(storage_a, storage_b, status, conflict_resolution=None,
list_b = prepare_list(storage_b, b_href_to_status)
if bool(list_a) != bool(list_b) and status and not force_delete:
raise StorageEmpty(storage_b if list_a else storage_a)
raise StorageEmpty(empty_storage=(storage_b if list_a else storage_a))
a_ident_to_href = dict((x['ident'], href) for href, x in iteritems(list_a))
b_ident_to_href = dict((x['ident'], href) for href, x in iteritems(list_b))
@ -185,15 +185,15 @@ def action_conflict_resolve(ident):
.format(ident))
a_storage, list_a, a_ident_to_href = storages['a']
b_storage, list_b, b_ident_to_href = storages['b']
a_href = a_ident_to_href[ident]
b_href = b_ident_to_href[ident]
a_meta = list_a[a_href]
b_meta = list_b[b_href]
if a_meta['item'].raw == b_meta['item'].raw:
href_a = a_ident_to_href[ident]
href_b = b_ident_to_href[ident]
meta_a = list_a[href_a]
meta_b = list_b[href_b]
if meta_a['item'].raw == meta_b['item'].raw:
sync_logger.info('...same content on both sides.')
status[ident] = a_href, a_meta['etag'], b_href, b_meta['etag']
status[ident] = href_a, meta_a['etag'], href_b, meta_b['etag']
elif conflict_resolution is None:
raise SyncConflict()
raise SyncConflict(ident=ident, href_a=href_a, href_b=href_b)
elif conflict_resolution == 'a wins':
sync_logger.info('...{} wins.'.format(a_storage))
action_update(ident, 'a', 'b')(storages, status,