From 473596a5736b3f5a0b0997d05b1e9b2be7b50c0d Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sun, 16 Feb 2014 15:35:51 +0100 Subject: [PATCH] Storage API improvements --- vdirsyncer/storage/base.py | 23 ++++++++++++++++------- vdirsyncer/storage/filesystem.py | 21 ++++++++++++--------- vdirsyncer/storage/memory.py | 25 ++++++++++++++++--------- 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/vdirsyncer/storage/base.py b/vdirsyncer/storage/base.py index 6c1a313..1ebeaf0 100644 --- a/vdirsyncer/storage/base.py +++ b/vdirsyncer/storage/base.py @@ -29,20 +29,28 @@ class Storage(object): self.fileext = fileext self.item_class = item_class - def list_items(self): + def list(self): ''' :returns: list of (uid, etag) ''' raise NotImplementedError() - def get_items(self, uids): + def get(self, uid): ''' - :param uids: list of uids to fetch - :returns: list of (object, uid, etag) + :param uid: uid to fetch + :returns: (object, uid, etag) ''' raise NotImplementedError() - def item_exists(self, uid): + def get_multi(self, uids): + ''' + :param uids: list of uids to fetch + :returns: iterable of (object, uid, etag) + ''' + for uid in uids: + yield self.get(uid) + + def has(self, uid): ''' check if item exists :returns: True or False @@ -67,8 +75,9 @@ class Storage(object): ''' raise NotImplementedError() - def delete(self, uid): + def delete(self, uid, etag): ''' - Delete the object, raise exceptions on error, no return value + Delete the object, raise exceptions when etag doesn't match, no return + value ''' raise NotImplementedError() diff --git a/vdirsyncer/storage/filesystem.py b/vdirsyncer/storage/filesystem.py index 9a6948b..8a94f21 100644 --- a/vdirsyncer/storage/filesystem.py +++ b/vdirsyncer/storage/filesystem.py @@ -23,20 +23,19 @@ class FilesystemStorage(Storage): def _get_filepath(self, uid): return os.path.join(self.path, uid + self.fileext) - def list_items(self): + def list(self): for fname in os.listdir(self.path): fpath = os.path.join(self.path, fname) if os.path.isfile(fpath) and fname.endswith(self.fileext): uid = fname[:-len(self.fileext)] yield uid, os.path.getmtime(fpath) - def get_items(self, uids): - for uid in uids: - fpath = self._get_filepath(uid) - with open(fpath, 'rb') as f: - yield Item(f.read()), uid, os.path.getmtime(fpath) + def get(self, uid): + fpath = self._get_filepath(uid) + with open(fpath, 'rb') as f: + return Item(f.read()), uid, os.path.getmtime(fpath) - def item_exists(self, uid): + def has(self, uid): return os.path.isfile(self._get_filepath(uid)) def upload(self, obj): @@ -54,9 +53,13 @@ class FilesystemStorage(Storage): actual_etag = os.path.getmtime(fpath) if etag != actual_etag: raise exceptions.WrongEtagError(etag, actual_etag) + with open(fpath, 'wb') as f: f.write(obj.raw) return os.path.getmtime(fpath) - def delete(self, uid): - os.remove(self._get_filepath(uid)) + def delete(self, uid, etag): + fpath = self._get_filepath(uid) + if etag != os.path.getmtime(fpath): + raise exceptions.WrongEtagError(etag, actual_etag) + os.remove(fpath) diff --git a/vdirsyncer/storage/memory.py b/vdirsyncer/storage/memory.py index 8485c84..92c263a 100644 --- a/vdirsyncer/storage/memory.py +++ b/vdirsyncer/storage/memory.py @@ -19,16 +19,15 @@ class MemoryStorage(Storage): self.items = {} # uid => (etag, object) super(MemoryStorage, self).__init__(**kwargs) - def list_items(self): + def list(self): for uid, (etag, obj) in self.items.items(): yield uid, etag - def get_items(self, uids): - for uid in uids: - etag, obj = self.items[uid] - yield obj, uid, etag + def get(self, uid): + etag, obj = self.items[uid] + return obj, uid, etag - def item_exists(self, uid): + def has(self, uid): return uid in self.items def upload(self, obj): @@ -41,9 +40,17 @@ class MemoryStorage(Storage): def update(self, obj, etag): if obj.uid not in self.items: raise exceptions.NotFoundError(obj) - etag = datetime.datetime.now() - self.items[obj.uid] = (etag, obj) + actual_etag, _ = self.items[obj.uid] + if etag != actual_etag: + raise exceptions.WrongEtagError(etag, actual_etag) + + new_etag = datetime.datetime.now() + self.items[obj.uid] = (new_etag, obj) return etag - def delete(self, uid): + def delete(self, uid, etag): + if not self.has(uid): + raise exceptions.NotFoundError(uid) + if etag != self.items[uid][0]: + raise exceptions.WrongEtagError(etag) del self.items[uid]