mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-04-27 14:57:41 +00:00
Implement Storage.at_once()
This commit is contained in:
parent
d07fe8376e
commit
bdc66633b3
4 changed files with 53 additions and 5 deletions
|
|
@ -22,6 +22,7 @@ Version 0.4.2
|
||||||
- Catch harmless threading exceptions that occur when shutting down vdirsyncer.
|
- Catch harmless threading exceptions that occur when shutting down vdirsyncer.
|
||||||
See :gh:`167`.
|
See :gh:`167`.
|
||||||
- Vdirsyncer now depends on ``atomicwrites``.
|
- Vdirsyncer now depends on ``atomicwrites``.
|
||||||
|
- Massive performance improvements to ``singlefile``-storage.
|
||||||
|
|
||||||
Version 0.4.1
|
Version 0.4.1
|
||||||
=============
|
=============
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
from .. import exceptions
|
from .. import exceptions
|
||||||
|
|
@ -186,3 +187,23 @@ class Storage(with_metaclass(StorageMeta)):
|
||||||
a different etag or doesn't exist.
|
a different etag or doesn't exist.
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def at_once(self):
|
||||||
|
'''A contextmanager that buffers all writes.
|
||||||
|
|
||||||
|
Essentially, this::
|
||||||
|
|
||||||
|
s.upload(...)
|
||||||
|
s.update(...)
|
||||||
|
|
||||||
|
becomes this::
|
||||||
|
|
||||||
|
with s.at_once():
|
||||||
|
s.upload(...)
|
||||||
|
s.update(...)
|
||||||
|
|
||||||
|
Note that this removes guarantees about which exceptions are returned
|
||||||
|
when.
|
||||||
|
'''
|
||||||
|
yield
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
import contextlib
|
||||||
|
import functools
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from atomicwrites import atomic_write
|
from atomicwrites import atomic_write
|
||||||
|
|
@ -14,6 +16,15 @@ from ..utils.vobject import join_collection, split_collection
|
||||||
logger = log.get(__name__)
|
logger = log.get(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _write_after(f):
|
||||||
|
@functools.wraps(f)
|
||||||
|
def inner(self, *args, **kwargs):
|
||||||
|
rv = f(self, *args, **kwargs)
|
||||||
|
if not self._at_once:
|
||||||
|
self._write()
|
||||||
|
return rv
|
||||||
|
return inner
|
||||||
|
|
||||||
class SingleFileStorage(Storage):
|
class SingleFileStorage(Storage):
|
||||||
'''Save data in single local ``.vcf`` or ``.ics`` file.
|
'''Save data in single local ``.vcf`` or ``.ics`` file.
|
||||||
|
|
||||||
|
|
@ -75,6 +86,7 @@ class SingleFileStorage(Storage):
|
||||||
|
|
||||||
self.path = path
|
self.path = path
|
||||||
self.encoding = encoding
|
self.encoding = encoding
|
||||||
|
self._at_once = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_collection(cls, collection, **kwargs):
|
def create_collection(cls, collection, **kwargs):
|
||||||
|
|
@ -117,6 +129,7 @@ class SingleFileStorage(Storage):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise exceptions.NotFoundError(href)
|
raise exceptions.NotFoundError(href)
|
||||||
|
|
||||||
|
@_write_after
|
||||||
def upload(self, item):
|
def upload(self, item):
|
||||||
href = item.ident
|
href = item.ident
|
||||||
self.list()
|
self.list()
|
||||||
|
|
@ -124,9 +137,9 @@ class SingleFileStorage(Storage):
|
||||||
raise exceptions.AlreadyExistingError(href)
|
raise exceptions.AlreadyExistingError(href)
|
||||||
|
|
||||||
self._items[href] = item, item.hash
|
self._items[href] = item, item.hash
|
||||||
self._write()
|
|
||||||
return href, item.hash
|
return href, item.hash
|
||||||
|
|
||||||
|
@_write_after
|
||||||
def update(self, href, item, etag):
|
def update(self, href, item, etag):
|
||||||
self.list()
|
self.list()
|
||||||
if href not in self._items:
|
if href not in self._items:
|
||||||
|
|
@ -137,9 +150,9 @@ class SingleFileStorage(Storage):
|
||||||
raise exceptions.WrongEtagError(etag, actual_etag)
|
raise exceptions.WrongEtagError(etag, actual_etag)
|
||||||
|
|
||||||
self._items[href] = item, item.hash
|
self._items[href] = item, item.hash
|
||||||
self._write()
|
|
||||||
return item.hash
|
return item.hash
|
||||||
|
|
||||||
|
@_write_after
|
||||||
def delete(self, href, etag):
|
def delete(self, href, etag):
|
||||||
self.list()
|
self.list()
|
||||||
if href not in self._items:
|
if href not in self._items:
|
||||||
|
|
@ -150,7 +163,6 @@ class SingleFileStorage(Storage):
|
||||||
raise exceptions.WrongEtagError(etag, actual_etag)
|
raise exceptions.WrongEtagError(etag, actual_etag)
|
||||||
|
|
||||||
del self._items[href]
|
del self._items[href]
|
||||||
self._write()
|
|
||||||
|
|
||||||
def _write(self):
|
def _write(self):
|
||||||
if self._last_mtime is not None and \
|
if self._last_mtime is not None and \
|
||||||
|
|
@ -166,3 +178,15 @@ class SingleFileStorage(Storage):
|
||||||
finally:
|
finally:
|
||||||
self._items = None
|
self._items = None
|
||||||
self._last_mtime = None
|
self._last_mtime = None
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def at_once(self):
|
||||||
|
self._at_once = True
|
||||||
|
try:
|
||||||
|
yield self
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
self._write()
|
||||||
|
finally:
|
||||||
|
self._at_once = False
|
||||||
|
|
|
||||||
|
|
@ -170,8 +170,10 @@ def sync(storage_a, storage_b, status, conflict_resolution=None,
|
||||||
|
|
||||||
actions = list(_get_actions(storages, status))
|
actions = list(_get_actions(storages, status))
|
||||||
|
|
||||||
for action in actions:
|
with storage_a.at_once():
|
||||||
action(storages, status, conflict_resolution)
|
with storage_b.at_once():
|
||||||
|
for action in actions:
|
||||||
|
action(storages, status, conflict_resolution)
|
||||||
|
|
||||||
|
|
||||||
def _action_upload(ident, dest):
|
def _action_upload(ident, dest):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue