Move "obscure" exceptions into their own modules

This commit is contained in:
Markus Unterwaditzer 2014-05-03 00:15:51 +02:00
parent 233e8524ab
commit 9616f0bbfe
5 changed files with 36 additions and 38 deletions

View file

@ -240,7 +240,7 @@ class TestCaldavStorage(DavStorageTests):
monkeypatch.setattr('requests.sessions.Session.request', request) monkeypatch.setattr('requests.sessions.Session.request', request)
with pytest.raises(exceptions.StorageError): with pytest.raises(ValueError):
self.storage_class(**args) self.storage_class(**args)
assert len(calls) == 1 assert len(calls) == 1

View file

@ -10,10 +10,9 @@
import pytest import pytest
from . import assert_item_equals, normalize_item from . import assert_item_equals, normalize_item
import vdirsyncer.exceptions as exceptions
from vdirsyncer.storage.base import Item from vdirsyncer.storage.base import Item
from vdirsyncer.storage.memory import MemoryStorage from vdirsyncer.storage.memory import MemoryStorage
from vdirsyncer.sync import sync from vdirsyncer.sync import sync, SyncConflict, StorageEmpty
def empty_storage(x): def empty_storage(x):
@ -51,7 +50,7 @@ def test_missing_status_and_different_items():
item2 = Item(u'UID:1\nhoho') item2 = Item(u'UID:1\nhoho')
a.upload(item1) a.upload(item1)
b.upload(item2) b.upload(item2)
with pytest.raises(exceptions.SyncConflict): with pytest.raises(SyncConflict):
sync(a, b, status) sync(a, b, status)
assert not status assert not status
sync(a, b, status, conflict_resolution='a wins') sync(a, b, status, conflict_resolution='a wins')
@ -137,7 +136,7 @@ def test_conflict_resolution_both_etags_new(winning_storage):
assert status assert status
a.update(href_a, Item(u'UID:1\nitem a'), etag_a) a.update(href_a, Item(u'UID:1\nitem a'), etag_a)
b.update(href_b, Item(u'UID:1\nitem b'), etag_b) 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)
sync(a, b, status, conflict_resolution='{} wins'.format(winning_storage)) sync(a, b, status, conflict_resolution='{} wins'.format(winning_storage))
item_a, _ = a.get(href_a) item_a, _ = a.get(href_a)
@ -203,8 +202,8 @@ def test_empty_storage_dataloss():
a.upload(Item(u'UID:2')) a.upload(Item(u'UID:2'))
status = {} status = {}
sync(a, b, status) sync(a, b, status)
with pytest.raises(exceptions.StorageEmpty): with pytest.raises(StorageEmpty):
sync(MemoryStorage(), b, status) sync(MemoryStorage(), b, status)
with pytest.raises(exceptions.StorageEmpty): with pytest.raises(StorageEmpty):
sync(a, MemoryStorage(), status) sync(a, MemoryStorage(), status)

View file

@ -3,6 +3,9 @@
vdirsyncer.exceptions vdirsyncer.exceptions
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
Contains exception classes used by vdirsyncer. Not all exceptions are here,
only the most commonly used ones.
:copyright: (c) 2014 Markus Unterwaditzer :copyright: (c) 2014 Markus Unterwaditzer
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
''' '''
@ -33,29 +36,3 @@ class AlreadyExistingError(PreconditionFailed):
class WrongEtagError(PreconditionFailed): class WrongEtagError(PreconditionFailed):
'''Wrong etag''' '''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]

View file

@ -84,7 +84,7 @@ class DavStorage(Storage):
) )
response.raise_for_status() response.raise_for_status()
if self.dav_header not in response.headers.get('DAV', ''): 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): def _default_headers(self):
return { return {

View file

@ -22,6 +22,28 @@ from .utils import iteritems, itervalues
sync_logger = log.get(__name__) 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): def prepare_list(storage, href_to_status):
download = [] download = []
for href, etag in storage.list(): 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. If this is the first sync, an empty dictionary should be provided.
:param conflict_resolution: Either 'a wins' or 'b wins'. If none is :param conflict_resolution: Either 'a wins' or 'b wins'. If none is
provided, the sync function will raise 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 :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 safety. Setting this parameter to ``True`` disables this safety
measure. 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)) list_b = dict(prepare_list(storage_b, b_href_to_status))
if bool(list_a) != bool(list_b) and status and not force_delete: 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)) 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)) 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.') sync_logger.info('...same content on both sides.')
status[uid] = a_href, a_meta['etag'], b_href, b_meta['etag'] status[uid] = a_href, a_meta['etag'], b_href, b_meta['etag']
elif conflict_resolution is None: elif conflict_resolution is None:
raise exceptions.SyncConflict() raise SyncConflict()
elif conflict_resolution == 'a wins': elif conflict_resolution == 'a wins':
sync_logger.info('...{} wins.'.format(a_storage)) sync_logger.info('...{} wins.'.format(a_storage))
action_update(uid, 'a', 'b')(storages, status, conflict_resolution) action_update(uid, 'a', 'b')(storages, status, conflict_resolution)