Improvements to singlefile storage

This commit is contained in:
Markus Unterwaditzer 2014-10-19 15:29:42 +02:00
parent 2fd74c8cbe
commit 75bd145507

View file

@ -8,6 +8,7 @@
''' '''
import collections import collections
import os
from .. import exceptions, log from .. import exceptions, log
from .base import Item, Storage from .base import Item, Storage
@ -27,9 +28,14 @@ class SingleFileStorage(Storage):
.. versionadded:: 0.1.6 .. versionadded:: 0.1.6
.. note:: .. note::
This storage has many raceconditions and is very slow. What this This storage has many raceconditions, which basically means that you
basically means is that you shouldn't use this storage unless you have should avoid changing the file with another program (or even running
to (e.g. you use a calendar client which requires it) any programs which might modify the file) while vdirsyncer is running.
Also it is currently very slow, so you should consider limiting the
amount of items you synchronize. In combination with
:py:class:`vdirsyncer.storage.CaldavStorage` this can be achieved via
the ``start_date`` and ``end_date`` parameters.
:param path: The filepath to the file to be written to. :param path: The filepath to the file to be written to.
:param encoding: Which encoding the file should use. Defaults to UTF-8. :param encoding: Which encoding the file should use. Defaults to UTF-8.
@ -61,6 +67,7 @@ class SingleFileStorage(Storage):
_read_mode = 'rb' _read_mode = 'rb'
_items = None _items = None
_last_mtime = None
def __init__(self, path, encoding='utf-8', create=True, **kwargs): def __init__(self, path, encoding='utf-8', create=True, **kwargs):
super(SingleFileStorage, self).__init__(**kwargs) super(SingleFileStorage, self).__init__(**kwargs)
@ -85,12 +92,13 @@ class SingleFileStorage(Storage):
self._items = collections.OrderedDict() self._items = collections.OrderedDict()
try: try:
self._last_mtime = os.path.getmtime(self.path)
with open(self.path, self._read_mode) as f: with open(self.path, self._read_mode) as f:
text = f.read().decode(self.encoding) text = f.read().decode(self.encoding)
except IOError as e: except OSError as e:
import errno import errno
if e.errno != errno.ENOENT or not self.create: # file not found if e.errno != errno.ENOENT or not self.create: # file not found
raise raise IOError(e)
text = None text = None
if not text: if not text:
@ -148,6 +156,10 @@ class SingleFileStorage(Storage):
self._write() self._write()
def _write(self): def _write(self):
if self._last_mtime is not None and \
self._last_mtime != os.path.getmtime(self.path):
raise exceptions.PreconditionFailed(
'Some other program modified the file {r!}'.format(path))
text = join_collection( text = join_collection(
(item.raw for item, etag in itervalues(self._items)), (item.raw for item, etag in itervalues(self._items)),
) )
@ -156,3 +168,4 @@ class SingleFileStorage(Storage):
f.write(text.encode(self.encoding)) f.write(text.encode(self.encoding))
finally: finally:
self._items = None self._items = None
self._last_mtime = None