mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-04-27 14:57:41 +00:00
Enforce read-only attribute
This commit is contained in:
parent
1d8c606005
commit
9b442ebcf3
4 changed files with 39 additions and 3 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -44,3 +44,7 @@ class AlreadyExistingError(PreconditionFailed):
|
||||||
|
|
||||||
class WrongEtagError(PreconditionFailed):
|
class WrongEtagError(PreconditionFailed):
|
||||||
'''Wrong etag'''
|
'''Wrong etag'''
|
||||||
|
|
||||||
|
|
||||||
|
class ReadOnlyError(Error):
|
||||||
|
'''Storage is read-only.'''
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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', (), {})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue