Unify terminology obj => item

This commit is contained in:
Markus Unterwaditzer 2014-04-06 00:21:28 +02:00
parent 2b81ba4ffd
commit 7f01b22642
7 changed files with 75 additions and 73 deletions

View file

@ -44,9 +44,9 @@ class StorageTests(object):
assert isinstance(href, (str, unicode)) assert isinstance(href, (str, unicode))
assert isinstance(etag, (str, unicode)) assert isinstance(etag, (str, unicode))
assert s.has(href) assert s.has(href)
obj, etag2 = s.get(href) item, etag2 = s.get(href)
assert etag == etag2 assert etag == etag2
assert 'UID:{}'.format(obj.uid) in obj.raw assert 'UID:{}'.format(item.uid) in item.raw
def test_upload_already_existing(self): def test_upload_already_existing(self):
s = self._get_storage() s = self._get_storage()
@ -81,10 +81,10 @@ class StorageTests(object):
def test_wrong_etag(self): def test_wrong_etag(self):
s = self._get_storage() s = self._get_storage()
obj = self._create_bogus_item(1) item = self._create_bogus_item(1)
href, etag = s.upload(obj) href, etag = s.upload(item)
with pytest.raises(exceptions.PreconditionFailed): with pytest.raises(exceptions.PreconditionFailed):
s.update(href, obj, '"lolnope"') s.update(href, item, '"lolnope"')
with pytest.raises(exceptions.PreconditionFailed): with pytest.raises(exceptions.PreconditionFailed):
s.delete(href, '"lolnope"') s.delete(href, '"lolnope"')

View file

@ -135,10 +135,10 @@ def test_conflict_resolution_both_etags_new():
with pytest.raises(exceptions.SyncConflict): with pytest.raises(exceptions.SyncConflict):
sync(a, b, status) sync(a, b, status)
sync(a, b, status, conflict_resolution='a wins') sync(a, b, status, conflict_resolution='a wins')
obj_a, _ = a.get(href_a) item_a, _ = a.get(href_a)
obj_b, _ = b.get(href_b) item_b, _ = b.get(href_b)
assert_item_equals(obj_a, obj_b) assert_item_equals(item_a, item_b)
n = normalize_item(obj_a) n = normalize_item(item_a)
assert u'UID:1' in n assert u'UID:1' in n
assert u'ASDASD' in n assert u'ASDASD' in n

View file

@ -29,15 +29,17 @@ class Storage(object):
implement. implement.
Terminology: Terminology:
- UID: Global identifier of the item, across storages. - ITEM: Instance of the Item class, represents a calendar event, task or
- HREF: Per-storage identifier of item, might be UID. The reason items contact.
aren't just referenced by their UID is because the CalDAV and CardDAV - UID: String; Global identifier of the item, across storages.
specifications make this imperformant to implement. - HREF: String; Per-storage identifier of item, might be UID. The reason
- ETAG: Checksum of item, or something similar that changes when the items aren't just referenced by their UID is because the CalDAV and
object does. CardDAV specifications make this imperformant to implement.
- ETAG: String; Checksum of item, or something similar that changes when the
item does.
All of the above properties should be strings. If bytestrings, an ASCII Strings can be either unicode strings or bytestrings. If bytestrings, an
encoding is assumed. ASCII encoding is assumed.
:param collection: If None, the given URL or path is already directly :param collection: If None, the given URL or path is already directly
referring to a collection. Otherwise it will be treated as a basepath referring to a collection. Otherwise it will be treated as a basepath
@ -77,7 +79,7 @@ class Storage(object):
def get(self, href): def get(self, href):
''' '''
:param href: href to fetch :param href: href to fetch
:returns: (object, etag) :returns: (item, etag)
''' '''
raise NotImplementedError() raise NotImplementedError()
@ -86,11 +88,11 @@ class Storage(object):
:param hrefs: list of hrefs to fetch :param hrefs: list of hrefs to fetch
:raises: :exc:`vdirsyncer.exceptions.PreconditionFailed` if one of the :raises: :exc:`vdirsyncer.exceptions.PreconditionFailed` if one of the
items couldn't be found. items couldn't be found.
:returns: iterable of (href, obj, etag) :returns: iterable of (href, item, etag)
''' '''
for href in hrefs: for href in hrefs:
obj, etag = self.get(href) item, etag = self.get(href)
yield href, obj, etag yield href, item, etag
def has(self, href): def has(self, href):
''' '''
@ -99,17 +101,17 @@ class Storage(object):
''' '''
raise NotImplementedError() raise NotImplementedError()
def upload(self, obj): def upload(self, item):
''' '''
Upload a new object, raise Upload a new item, raise
:exc:`vdirsyncer.exceptions.PreconditionFailed` if it already exists. :exc:`vdirsyncer.exceptions.PreconditionFailed` if it already exists.
:returns: (href, etag) :returns: (href, etag)
''' '''
raise NotImplementedError() raise NotImplementedError()
def update(self, href, obj, etag): def update(self, href, item, etag):
''' '''
Update the object, raise Update the item, raise
:exc:`vdirsyncer.exceptions.PreconditionFailed` if the etag on the :exc:`vdirsyncer.exceptions.PreconditionFailed` if the etag on the
server doesn't match the given etag or if the item doesn't exist. server doesn't match the given etag or if the item doesn't exist.
@ -119,7 +121,7 @@ class Storage(object):
def delete(self, href, etag): def delete(self, href, etag):
''' '''
Delete the object by href, raise Delete the item by href, raise
:exc:`vdirsyncer.exceptions.PreconditionFailed` when item has a :exc:`vdirsyncer.exceptions.PreconditionFailed` when item has a
different etag or doesn't exist. different etag or doesn't exist.
''' '''

View file

@ -103,9 +103,9 @@ class DavStorage(HttpStorageBase):
response.raise_for_status() response.raise_for_status()
def get(self, href): def get(self, href):
((actual_href, obj, etag),) = self.get_multi([href]) ((actual_href, item, etag),) = self.get_multi([href])
assert href == actual_href assert href == actual_href
return obj, etag return item, etag
def get_multi(self, hrefs): def get_multi(self, hrefs):
if not hrefs: if not hrefs:
@ -129,7 +129,7 @@ class DavStorage(HttpStorageBase):
for element in root.iter('{DAV:}response'): for element in root.iter('{DAV:}response'):
href = self._normalize_href( href = self._normalize_href(
element.find('{DAV:}href').text.decode(response.encoding)) element.find('{DAV:}href').text.decode(response.encoding))
obj = element \ raw = element \
.find('{DAV:}propstat') \ .find('{DAV:}propstat') \
.find('{DAV:}prop') \ .find('{DAV:}prop') \
.find(self.get_multi_data_query).text .find(self.get_multi_data_query).text
@ -137,11 +137,11 @@ class DavStorage(HttpStorageBase):
.find('{DAV:}propstat') \ .find('{DAV:}propstat') \
.find('{DAV:}prop') \ .find('{DAV:}prop') \
.find('{DAV:}getetag').text .find('{DAV:}getetag').text
if isinstance(obj, bytes): if isinstance(raw, bytes):
obj = obj.decode(response.encoding) raw = raw.decode(response.encoding)
if isinstance(etag, bytes): if isinstance(etag, bytes):
etag = etag.decode(response.encoding) etag = etag.decode(response.encoding)
rv.append((href, Item(obj), etag)) rv.append((href, Item(raw), etag))
try: try:
hrefs_left.remove(href) hrefs_left.remove(href)
except KeyError: except KeyError:
@ -159,7 +159,7 @@ class DavStorage(HttpStorageBase):
else: else:
return True return True
def _put(self, href, obj, etag): def _put(self, href, item, etag):
headers = self._default_headers() headers = self._default_headers()
headers['Content-Type'] = self.item_mimetype headers['Content-Type'] = self.item_mimetype
if etag is None: if etag is None:
@ -171,25 +171,25 @@ class DavStorage(HttpStorageBase):
response = self._request( response = self._request(
'PUT', 'PUT',
href, href,
data=obj.raw.encode('utf-8'), data=item.raw.encode('utf-8'),
headers=headers headers=headers
) )
self._check_response(response) self._check_response(response)
etag = response.headers.get('etag', None) etag = response.headers.get('etag', None)
if not etag: if not etag:
obj2, etag = self.get(href) item2, etag = self.get(href)
assert obj2.uid == obj.uid assert item2.uid == item.uid
return href, etag return href, etag
def update(self, href, obj, etag): def update(self, href, item, etag):
href = self._normalize_href(href) href = self._normalize_href(href)
if etag is None: if etag is None:
raise ValueError('etag must be given and must not be None.') raise ValueError('etag must be given and must not be None.')
return self._put(href, obj, etag) return self._put(href, item, etag)
def upload(self, obj): def upload(self, item):
href = self._get_href(obj.uid) href = self._get_href(item.uid)
return self._put(href, obj, None) return self._put(href, item, None)
def delete(self, href, etag): def delete(self, href, etag):
href = self._normalize_href(href) href = self._normalize_href(href)

View file

@ -76,28 +76,28 @@ class FilesystemStorage(Storage):
def has(self, href): def has(self, href):
return os.path.isfile(self._get_filepath(href)) return os.path.isfile(self._get_filepath(href))
def upload(self, obj): def upload(self, item):
href = self._get_href(obj.uid) href = self._get_href(item.uid)
fpath = self._get_filepath(href) fpath = self._get_filepath(href)
if os.path.exists(fpath): if os.path.exists(fpath):
raise exceptions.AlreadyExistingError(obj.uid) raise exceptions.AlreadyExistingError(item.uid)
with open(fpath, 'wb+') as f: with open(fpath, 'wb+') as f:
f.write(obj.raw.encode(self.encoding)) f.write(item.raw.encode(self.encoding))
return href, _get_etag(fpath) return href, _get_etag(fpath)
def update(self, href, obj, etag): def update(self, href, item, etag):
fpath = self._get_filepath(href) fpath = self._get_filepath(href)
if href != self._get_href(obj.uid): if href != self._get_href(item.uid):
logger.warning('href != uid + fileext: href={}; uid={}' logger.warning('href != uid + fileext: href={}; uid={}'
.format(href, obj.uid)) .format(href, item.uid))
if not os.path.exists(fpath): if not os.path.exists(fpath):
raise exceptions.NotFoundError(obj.uid) raise exceptions.NotFoundError(item.uid)
actual_etag = _get_etag(fpath) actual_etag = _get_etag(fpath)
if etag != actual_etag: if etag != actual_etag:
raise exceptions.WrongEtagError(etag, actual_etag) raise exceptions.WrongEtagError(etag, actual_etag)
with open(fpath, 'wb') as f: with open(fpath, 'wb') as f:
f.write(obj.raw.encode('utf-8')) f.write(item.raw.encode('utf-8'))
return _get_etag(fpath) return _get_etag(fpath)
def delete(self, href, etag): def delete(self, href, etag):

View file

@ -23,37 +23,37 @@ class MemoryStorage(Storage):
''' '''
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.items = {} # href => (etag, object) self.items = {} # href => (etag, item)
super(MemoryStorage, self).__init__(**kwargs) super(MemoryStorage, self).__init__(**kwargs)
def list(self): def list(self):
for href, (etag, obj) in self.items.items(): for href, (etag, item) in self.items.items():
yield href, etag yield href, etag
def get(self, href): def get(self, href):
etag, obj = self.items[href] etag, item = self.items[href]
return obj, etag return item, etag
def has(self, href): def has(self, href):
return href in self.items return href in self.items
def upload(self, obj): def upload(self, item):
href = self._get_href(obj.uid) href = self._get_href(item.uid)
if href in self.items: if href in self.items:
raise exceptions.AlreadyExistingError(obj.uid) raise exceptions.AlreadyExistingError(item.uid)
etag = _get_etag() etag = _get_etag()
self.items[href] = (etag, obj) self.items[href] = (etag, item)
return href, etag return href, etag
def update(self, href, obj, etag): def update(self, href, item, etag):
if href != self._get_href(obj.uid) or href not in self.items: if href != self._get_href(item.uid) or href not in self.items:
raise exceptions.NotFoundError(href) raise exceptions.NotFoundError(href)
actual_etag, _ = self.items[href] actual_etag, _ = self.items[href]
if etag != actual_etag: if etag != actual_etag:
raise exceptions.WrongEtagError(etag, actual_etag) raise exceptions.WrongEtagError(etag, actual_etag)
new_etag = _get_etag() new_etag = _get_etag()
self.items[href] = (new_etag, obj) self.items[href] = (new_etag, item)
return new_etag return new_etag
def delete(self, href, etag): def delete(self, href, etag):

View file

@ -23,21 +23,21 @@ def prepare_list(storage, href_to_uid):
if href in href_to_uid: if href in href_to_uid:
props['uid'] = href_to_uid[href] props['uid'] = href_to_uid[href]
else: else:
obj, new_etag = storage.get(href) item, new_etag = storage.get(href)
assert etag == new_etag assert etag == new_etag
props['uid'] = obj.uid props['uid'] = item.uid
props['obj'] = obj props['item'] = item
yield href, props yield href, props
def prefetch(storage, item_list, hrefs): def prefetch(storage, item_list, hrefs):
hrefs_to_prefetch = [] hrefs_to_prefetch = []
for href in hrefs: for href in hrefs:
if 'obj' not in item_list[href]: if 'item' not in item_list[href]:
hrefs_to_prefetch.append(href) hrefs_to_prefetch.append(href)
for href, obj, etag in storage.get_multi(hrefs_to_prefetch): for href, item, etag in storage.get_multi(hrefs_to_prefetch):
assert item_list[href]['etag'] == etag assert item_list[href]['etag'] == etag
item_list[href]['obj'] = obj item_list[href]['item'] = item
def sync(storage_a, storage_b, status, conflict_resolution=None): def sync(storage_a, storage_b, status, conflict_resolution=None):
@ -63,7 +63,7 @@ def sync(storage_a, storage_b, status, conflict_resolution=None):
(href_b, uid) (href_b, uid)
for uid, (href_a, etag_a, href_b, etag_b) in status.iteritems() for uid, (href_a, etag_a, href_b, etag_b) in status.iteritems()
) )
# href => {'etag': etag, 'obj': optional object, 'uid': uid} # href => {'etag': etag, 'item': optional item, 'uid': uid}
list_a = dict(prepare_list(storage_a, a_href_to_uid)) list_a = dict(prepare_list(storage_a, a_href_to_uid))
list_b = dict(prepare_list(storage_b, b_href_to_uid)) list_b = dict(prepare_list(storage_b, b_href_to_uid))
@ -97,8 +97,8 @@ def action_upload(uid, source, dest):
source_href = source_uid_to_href[uid] source_href = source_uid_to_href[uid]
source_etag = source_list[source_href]['etag'] source_etag = source_list[source_href]['etag']
obj = source_list[source_href]['obj'] item = source_list[source_href]['item']
dest_href, dest_etag = dest_storage.upload(obj) dest_href, dest_etag = dest_storage.upload(item)
source_status = (source_href, source_etag) source_status = (source_href, source_etag)
dest_status = (dest_href, dest_etag) dest_status = (dest_href, dest_etag)
@ -119,8 +119,8 @@ def action_update(uid, source, dest):
dest_href = dest_uid_to_href[uid] dest_href = dest_uid_to_href[uid]
old_etag = dest_list[dest_href]['etag'] old_etag = dest_list[dest_href]['etag']
obj = source_list[source_href]['obj'] item = source_list[source_href]['item']
dest_etag = dest_storage.update(dest_href, obj, old_etag) dest_etag = dest_storage.update(dest_href, item, old_etag)
source_status = (source_href, source_etag) source_status = (source_href, source_etag)
dest_status = (dest_href, dest_etag) dest_status = (dest_href, dest_etag)
@ -157,7 +157,7 @@ def action_conflict_resolve(uid):
b_href = b_uid_to_href[uid] b_href = b_uid_to_href[uid]
a_meta = list_a[a_href] a_meta = list_a[a_href]
b_meta = list_b[b_href] b_meta = list_b[b_href]
if a_meta['obj'].raw == b_meta['obj'].raw: if a_meta['item'].raw == b_meta['item'].raw:
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: