diff --git a/tests/storage/dav/test_main.py b/tests/storage/dav/test_main.py index 7a57f7d..cc7a062 100644 --- a/tests/storage/dav/test_main.py +++ b/tests/storage/dav/test_main.py @@ -240,7 +240,7 @@ class TestCaldavStorage(DavStorageTests): monkeypatch.setattr('requests.sessions.Session.request', request) - with pytest.raises(exceptions.StorageError): + with pytest.raises(ValueError): self.storage_class(**args) assert len(calls) == 1 diff --git a/tests/test_sync.py b/tests/test_sync.py index 7a0dac3..ec66524 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -10,10 +10,9 @@ import pytest from . import assert_item_equals, normalize_item -import vdirsyncer.exceptions as exceptions from vdirsyncer.storage.base import Item from vdirsyncer.storage.memory import MemoryStorage -from vdirsyncer.sync import sync +from vdirsyncer.sync import sync, SyncConflict, StorageEmpty def empty_storage(x): @@ -51,7 +50,7 @@ def test_missing_status_and_different_items(): item2 = Item(u'UID:1\nhoho') a.upload(item1) b.upload(item2) - with pytest.raises(exceptions.SyncConflict): + with pytest.raises(SyncConflict): sync(a, b, status) assert not status sync(a, b, status, conflict_resolution='a wins') @@ -137,7 +136,7 @@ def test_conflict_resolution_both_etags_new(winning_storage): assert status a.update(href_a, Item(u'UID:1\nitem a'), etag_a) b.update(href_b, Item(u'UID:1\nitem b'), etag_b) - with pytest.raises(exceptions.SyncConflict): + with pytest.raises(SyncConflict): sync(a, b, status) sync(a, b, status, conflict_resolution='{} wins'.format(winning_storage)) item_a, _ = a.get(href_a) @@ -203,8 +202,8 @@ def test_empty_storage_dataloss(): a.upload(Item(u'UID:2')) status = {} sync(a, b, status) - with pytest.raises(exceptions.StorageEmpty): + with pytest.raises(StorageEmpty): sync(MemoryStorage(), b, status) - with pytest.raises(exceptions.StorageEmpty): + with pytest.raises(StorageEmpty): sync(a, MemoryStorage(), status) diff --git a/vdirsyncer/exceptions.py b/vdirsyncer/exceptions.py index eda0410..af35c28 100644 --- a/vdirsyncer/exceptions.py +++ b/vdirsyncer/exceptions.py @@ -3,6 +3,9 @@ vdirsyncer.exceptions ~~~~~~~~~~~~~~~~~~~~~ + Contains exception classes used by vdirsyncer. Not all exceptions are here, + only the most commonly used ones. + :copyright: (c) 2014 Markus Unterwaditzer :license: MIT, see LICENSE for more details. ''' @@ -33,29 +36,3 @@ class AlreadyExistingError(PreconditionFailed): class WrongEtagError(PreconditionFailed): '''Wrong etag''' - - -class StorageError(Error): - '''Internal or initialization errors with storage.''' - - -class SyncError(Error): - '''Errors related to synchronization.''' - - -class SyncConflict(SyncError): - ''' - Two items changed since the last sync, they now have different contents and - no conflict resolution method was given. - ''' - - -class StorageEmpty(SyncError): - ''' - One storage unexpectedly got completely empty between two synchronizations. - The first argument is the empty storage. - ''' - - @property - def empty_storage(self): - return self.args[0] diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index 21f1f7d..4715a21 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -84,7 +84,7 @@ class DavStorage(Storage): ) response.raise_for_status() if self.dav_header not in response.headers.get('DAV', ''): - raise exceptions.StorageError('URL is not a collection') + raise ValueError('URL is not a collection') def _default_headers(self): return { diff --git a/vdirsyncer/sync.py b/vdirsyncer/sync.py index 07b0839..a048c1f 100644 --- a/vdirsyncer/sync.py +++ b/vdirsyncer/sync.py @@ -22,6 +22,28 @@ from .utils import iteritems, itervalues sync_logger = log.get(__name__) +class SyncError(exceptions.Error): + '''Errors related to synchronization.''' + + +class SyncConflict(SyncError): + ''' + Two items changed since the last sync, they now have different contents and + no conflict resolution method was given. + ''' + + +class StorageEmpty(SyncError): + ''' + One storage unexpectedly got completely empty between two synchronizations. + The first argument is the empty storage. + ''' + + @property + def empty_storage(self): + return self.args[0] + + def prepare_list(storage, href_to_status): download = [] for href, etag in storage.list(): @@ -55,9 +77,9 @@ def sync(storage_a, storage_b, status, conflict_resolution=None, If this is the first sync, an empty dictionary should be provided. :param conflict_resolution: Either 'a wins' or 'b wins'. If none is provided, the sync function will raise - :py:exc:`vdirsyncer.exceptions.SyncConflict`. + :py:exc:`SyncConflict`. :param force_delete: When one storage got completely emptied between two - syncs, :py:exc:`vdirsyncer.exceptions.StorageEmpty` is raised for + syncs, :py:exc:`StorageEmpty` is raised for safety. Setting this parameter to ``True`` disables this safety measure. ''' @@ -74,7 +96,7 @@ def sync(storage_a, storage_b, status, conflict_resolution=None, list_b = dict(prepare_list(storage_b, b_href_to_status)) if bool(list_a) != bool(list_b) and status and not force_delete: - raise exceptions.StorageEmpty(storage_b if list_a else storage_a) + raise StorageEmpty(storage_b if list_a else storage_a) a_uid_to_href = dict((x['uid'], href) for href, x in iteritems(list_a)) b_uid_to_href = dict((x['uid'], href) for href, x in iteritems(list_b)) @@ -165,7 +187,7 @@ def action_conflict_resolve(uid): sync_logger.info('...same content on both sides.') status[uid] = a_href, a_meta['etag'], b_href, b_meta['etag'] elif conflict_resolution is None: - raise exceptions.SyncConflict() + raise SyncConflict() elif conflict_resolution == 'a wins': sync_logger.info('...{} wins.'.format(a_storage)) action_update(uid, 'a', 'b')(storages, status, conflict_resolution)