From 76c7f034a7be6b64f65ab38a4bae93f1246cca8e Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 24 Jan 2015 16:33:32 +0100 Subject: [PATCH] Catch IdentConflict Fix #170 --- tests/test_cli.py | 35 +++++++++++++++++++++++++++++++++++ vdirsyncer/cli/utils.py | 19 ++++++++++++++----- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index c95b8c7..5cc63c9 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -483,6 +483,41 @@ def test_create_collections(tmpdir, runner): set('abc') +def test_ident_conflict(tmpdir, runner): + runner.write_with_general(dedent(''' + [pair foobar] + a = foo + b = bar + + [storage foo] + type = filesystem + path = {base}/foo/ + fileext = .txt + + [storage bar] + type = filesystem + path = {base}/bar/ + fileext = .txt + '''.format(base=str(tmpdir)))) + + foo = tmpdir.mkdir('foo') + bar = tmpdir.mkdir('bar') + + foo.join('one.txt').write('UID:1') + foo.join('two.txt').write('UID:1') + foo.join('three.txt').write('UID:1') + + result = runner.invoke(['sync']) + assert result.exception + assert ('error: foobar: Storage "foo" contains multiple items with the ' + 'same UID or even content') in result.output + assert sorted([ + 'one.txt' in result.output, + 'two.txt' in result.output, + 'three.txt' in result.output, + ]) == [False, True, True] + + def test_parse_config_value(capsys): invalid = object() diff --git a/vdirsyncer/cli/utils.py b/vdirsyncer/cli/utils.py index 0cdaaa4..af7e735 100644 --- a/vdirsyncer/cli/utils.py +++ b/vdirsyncer/cli/utils.py @@ -12,7 +12,7 @@ from itertools import chain from .. import DOCS_HOME, PROJECT_HOME, exceptions, log from ..doubleclick import click from ..storage import storage_names -from ..sync import StorageEmpty, SyncConflict +from ..sync import IdentConflict, StorageEmpty, SyncConflict from ..utils import atomic_write, expand_path, get_class_init_args from ..utils.compat import text_type @@ -69,13 +69,22 @@ def handle_cli_error(status_name='sync'): 'Item href on side B: {e.href_b}\n' .format(status_name=status_name, e=e, docs=DOCS_HOME) ) + except IdentConflict as e: + cli_logger.error( + '{status_name}: Storage "{name}" contains multiple items with the ' + 'same UID or even content. Vdirsyncer will now abort the ' + 'synchronization of this collection, because the fix for this is ' + 'not clear; It could be the result of a badly behaving server.\n' + '\n{href_list}\n' + .format(status_name=status_name, + name=e.storage.instance_name, + href_list='\n'.join(map(repr, e.hrefs))) + ) except (click.Abort, KeyboardInterrupt, JobFailed): pass except Exception as e: cli_logger.exception('Unhandled exception occured while syncing {}.' .format(status_name)) - else: - return True def validate_section_name(name, section_type): @@ -470,9 +479,9 @@ class WorkerQueue(object): try: func(wq=self) - except: + except Exception as e: if not _handle_cli_error(): - self._exceptions.append(sys.exc_info()[1]) + self._exceptions.append(e) finally: self._queue.task_done()