mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-04-14 12:15:53 +00:00
Restructure status layout for more flexibility
This commit is contained in:
parent
779ff094bb
commit
b144ae1701
2 changed files with 50 additions and 22 deletions
|
|
@ -126,7 +126,7 @@ def test_already_synced():
|
|||
a.upload(item)
|
||||
b.upload(item)
|
||||
status = {
|
||||
'1': ('1.a', a.get('1.a')[1], '1.b', b.get('1.b')[1])
|
||||
'1': ({'href': '1.a', 'etag': a.get('1.a')[1]}, {'href': '1.b', 'etag': b.get('1.b')[1]})
|
||||
}
|
||||
old_status = dict(status)
|
||||
a.update = b.update = a.upload = b.upload = \
|
||||
|
|
@ -195,7 +195,13 @@ def test_conflict_resolution_new_etags_without_changes():
|
|||
href_b, etag_b = b.upload(item)
|
||||
status = {'1': (href_a, 'BOGUS_a', href_b, 'BOGUS_b')}
|
||||
sync(a, b, status)
|
||||
assert status == {'1': (href_a, etag_a, href_b, etag_b)}
|
||||
assert status == {'1': ({
|
||||
'href': href_a,
|
||||
'etag': etag_a,
|
||||
}, {
|
||||
'href': href_b,
|
||||
'etag': etag_b
|
||||
})}
|
||||
|
||||
|
||||
def test_uses_get_multi(monkeypatch):
|
||||
|
|
@ -328,7 +334,7 @@ def test_moved_href():
|
|||
sync(a, b, status)
|
||||
assert len(status) == 1
|
||||
assert len(list(a.list())) == len(list(b.list())) == 1
|
||||
assert status['haha'][2] == 'haha'
|
||||
assert status['haha'][1]['href'] == 'haha'
|
||||
|
||||
|
||||
def test_unicode_hrefs():
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ class StorageSyncer(object):
|
|||
self.idents = None
|
||||
|
||||
def prepare_idents(self):
|
||||
href_to_status = dict((href, (ident, etag))
|
||||
for ident, (href, etag)
|
||||
href_to_status = dict((meta['href'], (ident, meta))
|
||||
for ident, meta
|
||||
in iteritems(self.status))
|
||||
|
||||
prefetch = {}
|
||||
|
|
@ -103,9 +103,9 @@ class StorageSyncer(object):
|
|||
|
||||
for href, etag in self.storage.list():
|
||||
props = {'href': href, 'etag': etag}
|
||||
ident, old_etag = href_to_status.get(href, (None, None))
|
||||
ident, old_meta = href_to_status.get(href, (None, None))
|
||||
assert etag is not None
|
||||
if etag != old_etag:
|
||||
if old_meta is None or etag != old_meta['etag']:
|
||||
# Either the item is completely new, or updated
|
||||
# In both cases we should prefetch
|
||||
prefetch[href] = props
|
||||
|
|
@ -129,10 +129,32 @@ class StorageSyncer(object):
|
|||
_store_props(ident, props)
|
||||
|
||||
def is_changed(self, ident):
|
||||
_, status_etag = self.status.get(ident, (None, None))
|
||||
return self.idents[ident]['etag'] != status_etag
|
||||
meta = self.status.get(ident, None)
|
||||
return meta is None or self.idents[ident]['etag'] != meta['etag']
|
||||
|
||||
|
||||
def _status_migrate(status):
|
||||
for ident in list(status):
|
||||
value = status[ident]
|
||||
if len(value) == 4:
|
||||
href_a, etag_a, href_b, etag_b = value
|
||||
|
||||
status[ident] = ({
|
||||
'href': href_a,
|
||||
'etag': etag_a,
|
||||
}, {
|
||||
'href': href_b,
|
||||
'etag': etag_b,
|
||||
})
|
||||
|
||||
def _compress_meta(meta):
|
||||
'''Make in-memory metadata suitable for disk storage by removing fetched
|
||||
item content'''
|
||||
return {
|
||||
'href': meta['href'],
|
||||
'etag': meta['etag']
|
||||
}
|
||||
|
||||
def sync(storage_a, storage_b, status, conflict_resolution=None,
|
||||
force_delete=False):
|
||||
'''Synchronizes two storages.
|
||||
|
|
@ -156,13 +178,15 @@ def sync(storage_a, storage_b, status, conflict_resolution=None,
|
|||
if storage_a.read_only and storage_b.read_only:
|
||||
raise BothReadOnly()
|
||||
|
||||
_status_migrate(status)
|
||||
|
||||
a_info = storage_a.syncer_class(storage_a, dict(
|
||||
(ident, (href_a, etag_a))
|
||||
for ident, (href_a, etag_a, href_b, etag_b) in iteritems(status)
|
||||
(ident, meta_a)
|
||||
for ident, (meta_a, meta_b) in iteritems(status)
|
||||
))
|
||||
b_info = storage_b.syncer_class(storage_b, dict(
|
||||
(ident, (href_b, etag_b))
|
||||
for ident, (href_a, etag_a, href_b, etag_b) in iteritems(status)
|
||||
(ident, meta_b)
|
||||
for ident, (meta_a, meta_b) in iteritems(status)
|
||||
))
|
||||
|
||||
a_info.prepare_idents()
|
||||
|
|
@ -182,9 +206,7 @@ def sync(storage_a, storage_b, status, conflict_resolution=None,
|
|||
|
||||
status.clear()
|
||||
for ident in uniq(itertools.chain(a_info.status, b_info.status)):
|
||||
href_a, etag_a = a_info.status[ident]
|
||||
href_b, etag_b = b_info.status[ident]
|
||||
status[ident] = href_a, etag_a, href_b, etag_b
|
||||
status[ident] = a_info.status[ident], b_info.status[ident]
|
||||
|
||||
|
||||
def _action_upload(ident, source, dest):
|
||||
|
|
@ -202,8 +224,8 @@ def _action_upload(ident, source, dest):
|
|||
item = source_meta['item']
|
||||
dest_href, dest_etag = dest.storage.upload(item)
|
||||
|
||||
source.status[ident] = source_meta['href'], source_meta['etag']
|
||||
dest.status[ident] = dest_href, dest_etag
|
||||
source.status[ident] = _compress_meta(source_meta)
|
||||
dest.status[ident] = {'href': dest_href, 'etag': dest_etag}
|
||||
|
||||
return inner
|
||||
|
||||
|
|
@ -226,8 +248,8 @@ def _action_update(ident, source, dest):
|
|||
dest_meta['etag'])
|
||||
assert isinstance(dest_etag, (bytes, text_type))
|
||||
|
||||
source.status[ident] = source_meta['href'], source_meta['etag']
|
||||
dest.status[ident] = dest_href, dest_etag
|
||||
source.status[ident] = _compress_meta(source_meta)
|
||||
dest.status[ident] = {'href': dest_href, 'etag': dest_etag}
|
||||
|
||||
return inner
|
||||
|
||||
|
|
@ -272,8 +294,8 @@ def _action_conflict_resolve(ident):
|
|||
|
||||
if meta_a['item'].hash == meta_b['item'].hash:
|
||||
sync_logger.info(u'...same content on both sides.')
|
||||
a.status[ident] = meta_a['href'], meta_a['etag']
|
||||
b.status[ident] = meta_b['href'], meta_b['etag']
|
||||
a.status[ident] = _compress_meta(meta_a)
|
||||
b.status[ident] = _compress_meta(meta_b)
|
||||
elif conflict_resolution is None:
|
||||
raise SyncConflict(ident=ident, href_a=meta_a['href'],
|
||||
href_b=meta_b['href'])
|
||||
|
|
|
|||
Loading…
Reference in a new issue