Enforce read-only attribute

This commit is contained in:
Markus Unterwaditzer 2014-12-27 11:22:48 +01:00
parent 1d8c606005
commit 9b442ebcf3
4 changed files with 39 additions and 3 deletions

View file

@ -9,6 +9,7 @@
import pytest import pytest
import vdirsyncer.exceptions as exceptions
from vdirsyncer.storage.base import Item from vdirsyncer.storage.base import Item
from vdirsyncer.storage.memory import MemoryStorage from vdirsyncer.storage.memory import MemoryStorage
from vdirsyncer.sync import BothReadOnly, StorageEmpty, SyncConflict, sync from vdirsyncer.sync import BothReadOnly, StorageEmpty, SyncConflict, sync
@ -251,10 +252,14 @@ def test_both_readonly():
def test_readonly(): def test_readonly():
a = MemoryStorage() a = MemoryStorage()
b = MemoryStorage(read_only=True) b = MemoryStorage()
status = {} status = {}
href_a, _ = a.upload(Item(u'UID:1')) href_a, _ = a.upload(Item(u'UID:1'))
href_b, _ = b.upload(Item(u'UID:2')) href_b, _ = b.upload(Item(u'UID:2'))
b.read_only = True
with pytest.raises(exceptions.ReadOnlyError):
b.upload(Item(u'UID:3'))
sync(a, b, status) sync(a, b, status)
assert len(status) == 2 and a.has(href_a) and not b.has(href_a) assert len(status) == 2 and a.has(href_a) and not b.has(href_a)
sync(a, b, status) sync(a, b, status)

View file

@ -44,3 +44,7 @@ class AlreadyExistingError(PreconditionFailed):
class WrongEtagError(PreconditionFailed): class WrongEtagError(PreconditionFailed):
'''Wrong etag''' '''Wrong etag'''
class ReadOnlyError(Error):
'''Storage is read-only.'''

View file

@ -7,13 +7,31 @@
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
''' '''
import functools
from .. import exceptions from .. import exceptions
from ..utils import uniq from ..utils import uniq
from ..utils.compat import with_metaclass
from ..utils.vobject import Item # noqa from ..utils.vobject import Item # noqa
class Storage(object): def mutating_storage_method(f):
@functools.wraps(f)
def inner(self, *args, **kwargs):
if self.read_only:
raise exceptions.ReadOnlyError('This storage is read-only.')
return f(self, *args, **kwargs)
return inner
class StorageMeta(type):
def __init__(cls, name, bases, d):
for method in ('update', 'upload', 'delete'):
setattr(cls, method, mutating_storage_method(getattr(cls, method)))
return super(StorageMeta, cls).__init__(name, bases, d)
class Storage(with_metaclass(StorageMeta)):
'''Superclass of all storages, mainly useful to summarize the interface to '''Superclass of all storages, mainly useful to summarize the interface to
implement. implement.
@ -63,8 +81,9 @@ class Storage(object):
if read_only is None: if read_only is None:
read_only = self.read_only read_only = self.read_only
if self.read_only and not read_only: if self.read_only and not read_only:
raise ValueError('This storage is read-only.') raise ValueError('This storage can only be read-only.')
self.read_only = bool(read_only) self.read_only = bool(read_only)
if collection and instance_name: if collection and instance_name:
instance_name = '{}/{}'.format(instance_name, collection) instance_name = '{}/{}'.format(instance_name, collection)
self.instance_name = instance_name self.instance_name = instance_name

View file

@ -27,3 +27,11 @@ else: # pragma: no cover
text_type = str text_type = str
iteritems = lambda x: x.items() iteritems = lambda x: x.items()
itervalues = lambda x: x.values() itervalues = lambda x: x.values()
def with_metaclass(meta, *bases):
'''Original code from six, by Benjamin Peterson.'''
class metaclass(meta):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
return type.__new__(metaclass, 'temporary_class', (), {})