mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-03-25 08:55:50 +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 vdirsyncer.exceptions as exceptions
|
||||
from vdirsyncer.storage.base import Item
|
||||
from vdirsyncer.storage.memory import MemoryStorage
|
||||
from vdirsyncer.sync import BothReadOnly, StorageEmpty, SyncConflict, sync
|
||||
|
|
@ -251,10 +252,14 @@ def test_both_readonly():
|
|||
|
||||
def test_readonly():
|
||||
a = MemoryStorage()
|
||||
b = MemoryStorage(read_only=True)
|
||||
b = MemoryStorage()
|
||||
status = {}
|
||||
href_a, _ = a.upload(Item(u'UID:1'))
|
||||
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)
|
||||
assert len(status) == 2 and a.has(href_a) and not b.has(href_a)
|
||||
sync(a, b, status)
|
||||
|
|
|
|||
|
|
@ -44,3 +44,7 @@ class AlreadyExistingError(PreconditionFailed):
|
|||
|
||||
class WrongEtagError(PreconditionFailed):
|
||||
'''Wrong etag'''
|
||||
|
||||
|
||||
class ReadOnlyError(Error):
|
||||
'''Storage is read-only.'''
|
||||
|
|
|
|||
|
|
@ -7,13 +7,31 @@
|
|||
:license: MIT, see LICENSE for more details.
|
||||
'''
|
||||
|
||||
import functools
|
||||
|
||||
from .. import exceptions
|
||||
from ..utils import uniq
|
||||
from ..utils.compat import with_metaclass
|
||||
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
|
||||
implement.
|
||||
|
|
@ -63,8 +81,9 @@ class Storage(object):
|
|||
if read_only is None:
|
||||
read_only = self.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)
|
||||
|
||||
if collection and instance_name:
|
||||
instance_name = '{}/{}'.format(instance_name, collection)
|
||||
self.instance_name = instance_name
|
||||
|
|
|
|||
|
|
@ -27,3 +27,11 @@ else: # pragma: no cover
|
|||
text_type = str
|
||||
iteritems = lambda x: x.items()
|
||||
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