diff options
author | Lars Wirzenius <liw@liw.fi> | 2017-10-06 19:09:39 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2017-10-09 21:00:41 +0300 |
commit | 4cd033e8f682f5a5ad2b64bc77b52b28f48e7020 (patch) | |
tree | 670d05be7da40b1000a158408ec79bef251ff77a | |
parent | 6bad303cda8caa13d871865edeb000c44379d8a3 (diff) | |
download | qvisqve-4cd033e8f682f5a5ad2b64bc77b52b28f48e7020.tar.gz |
Add: blob handling in ObjectStore
-rw-r--r-- | qvarn/__init__.py | 2 | ||||
-rw-r--r-- | qvarn/objstore.py | 87 | ||||
-rw-r--r-- | qvarn/objstore_tests.py | 36 |
3 files changed, 120 insertions, 5 deletions
diff --git a/qvarn/__init__.py b/qvarn/__init__.py index 21eac23..b2d6bb1 100644 --- a/qvarn/__init__.py +++ b/qvarn/__init__.py @@ -50,6 +50,8 @@ from .objstore import ( UnknownKey, WrongKeyType, KeyValueError, + NoSuchObject, + BlobKeyCollision, flatten_object, ) diff --git a/qvarn/objstore.py b/qvarn/objstore.py index 9449a74..aeab6bc 100644 --- a/qvarn/objstore.py +++ b/qvarn/objstore.py @@ -68,6 +68,12 @@ class ObjectStoreInterface: # pragma: no cover if key not in known_keys: raise UnknownKey(key) + def check_value_types(self, **keys): + known_keys = self.get_known_keys() + for key in keys: + if type(keys[key]) is not known_keys[key]: + raise KeyValueError(key, keys[key]) + def create_object(self, obj, auxtable=True, **keys): raise NotImplementedError() @@ -80,11 +86,21 @@ class ObjectStoreInterface: # pragma: no cover def find_objects(self, cond): raise NotImplementedError() + def create_blob(self, blob, subpath=None, **keys): + raise NotImplementedError() + + def get_blob(self, subpath=None, **keys): + raise NotImplementedError() + + def remove_blob(self, blob, subpath=None, **keys): + raise NotImplementedError() + class MemoryObjectStore(ObjectStoreInterface): def __init__(self): self._objs = [] + self._blobs = [] self._known_keys = {} def get_known_keys(self): @@ -97,17 +113,56 @@ class MemoryObjectStore(ObjectStoreInterface): self._known_keys = keys def create_object(self, obj, auxtable=True, **keys): - self.check_all_keys_are_allowed(**keys) qvarn.log.log( 'trace', msg_text='Creating object', object=repr(obj), keys=keys) - for key in keys: - if type(keys[key]) is not self._known_keys[key]: - raise KeyValueError(key, keys[key]) + self.check_all_keys_are_allowed(**keys) + self.check_value_types(**keys) + self._check_unique_object(**keys) + self._objs.append((obj, keys)) + def _check_unique_object(self, **keys): for _, k in self._objs: if self._keys_match(k, keys): raise KeyCollision(k) - self._objs.append((obj, keys)) + + def create_blob(self, blob, **keys): + qvarn.log.log('trace', msg_text='Creating blob', keys=keys) + subpath = keys.pop('subpath') + self.check_all_keys_are_allowed(**keys) + self.check_value_types(**keys) + self._check_unique_blob(subpath, **keys) + if not self.get_objects(**keys): + raise NoSuchObject(keys) + self._blobs.append((blob, subpath, keys)) + + def _check_unique_blob(self, subpath, **keys): + for _, s, k in self._blobs: + if self._keys_match(k, keys) and s == subpath: + raise BlobKeyCollision(subpath, k) + + def get_blob(self, **keys): + subpath = keys.pop('subpath') + self.check_all_keys_are_allowed(**keys) + self.check_value_types(**keys) + blobs = [ + b + for b, s, k in self._blobs + if self._keys_match(k, keys) and s == subpath + ] + assert len(blobs) <= 1 + if not blobs: + raise NoSuchObject(keys) + return blobs[0] + + def remove_blob(self, **keys): + subpath = keys.pop('subpath') + self.check_all_keys_are_allowed(**keys) + self.check_value_types(**keys) + self._blobs = [ + b + for b, s, k in self._blobs + if not self._keys_match(k, keys) or s != subpath + ] def remove_objects(self, **keys): self.check_all_keys_are_allowed(**keys) @@ -250,6 +305,15 @@ class PostgresObjectStore(ObjectStoreInterface): # pragma: no cover obj = row.pop('_obj') return keys, obj + def create_blob(self, blob, subpath=None, **keys): + raise NotImplementedError() + + def get_blob(self, subpath=None, **keys): + raise NotImplementedError() + + def remove_blob(self, blob, subpath=None, **keys): + raise NotImplementedError() + class KeyCollision(Exception): @@ -257,6 +321,13 @@ class KeyCollision(Exception): super().__init__('Cannot add object with same keys: %r' % keys) +class BlobKeyCollision(Exception): + + def __init__(self, subpath, keys): + super().__init__( + 'Cannot add blob with same keys: subpath=%s %r' % (subpath, keys)) + + class UnknownKey(Exception): def __init__(self, key): @@ -277,6 +348,12 @@ class KeyValueError(Exception): super().__init__('Key %r value %r has the wrong type' % (key, value)) +class NoSuchObject(Exception): + + def __init__(self, keys): + super().__init__('No object/blob with keys {}'.format(keys)) + + def flatten_object(obj): return list(sorted(set(_flatten(obj)))) diff --git a/qvarn/objstore_tests.py b/qvarn/objstore_tests.py index 5103d47..f8d631a 100644 --- a/qvarn/objstore_tests.py +++ b/qvarn/objstore_tests.py @@ -28,6 +28,8 @@ class ObjectStoreTests(unittest.TestCase): self.obj2 = { 'name': 'this is my other object', } + self.blob1 = 'my first blob' + self.blob2 = 'my other blob' def create_store(self, **keys): store = qvarn.MemoryObjectStore() @@ -123,6 +125,40 @@ class ObjectStoreTests(unittest.TestCase): [({'key': '1st'}, self.obj1)] ) + def test_has_no_blob_initially(self): + store = self.create_store(key=str) + store.create_object(self.obj1, key='1st') + with self.assertRaises(qvarn.NoSuchObject): + store.get_blob(key='1st', subpath='blob') + + def test_add_blob_to_nonexistent_parent_fails(self): + store = self.create_store(key=str) + store.create_object(self.obj1, key='1st') + with self.assertRaises(qvarn.NoSuchObject): + store.create_blob(self.blob1, key='2nd', subpath='blob') + + def test_adds_blob(self): + store = self.create_store(key=str) + store.create_object(self.obj1, key='1st') + store.create_blob(self.blob1, key='1st', subpath='blob') + blob = store.get_blob(key='1st', subpath='blob') + self.assertEqual(blob, self.blob1) + + def test_add_blob_twice_fails(self): + store = self.create_store(key=str) + store.create_object(self.obj1, key='1st') + store.create_blob(self.blob1, key='1st', subpath='blob') + with self.assertRaises(qvarn.BlobKeyCollision): + store.create_blob(self.blob1, key='1st', subpath='blob') + + def test_removes_blob(self): + store = self.create_store(key=str) + store.create_object(self.obj1, key='1st') + store.create_blob(self.blob1, key='1st', subpath='blob') + store.remove_blob(key='1st', subpath='blob') + with self.assertRaises(qvarn.NoSuchObject): + store.get_blob(key='1st', subpath='blob') + class FlattenObjectsTests(unittest.TestCase): |