From 581f6cd53f57599326439dccb355e882e7858faa Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sun, 14 Jul 2019 12:50:04 +0300 Subject: Add: MemoryPersitentState --- ick2/__init__.py | 4 ++-- ick2/apibase.py | 4 ++-- ick2/persistent.py | 61 ++++++++++++------------------------------------ ick2/persistent_tests.py | 30 ++++++++++++++---------- ick2/projectapi_tests.py | 5 ++-- ick2/workapi_tests.py | 5 ++-- 6 files changed, 41 insertions(+), 68 deletions(-) (limited to 'ick2') diff --git a/ick2/__init__.py b/ick2/__init__.py index f489396..0016f13 100644 --- a/ick2/__init__.py +++ b/ick2/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2018 Lars Wirzenius +# Copyright (C) 2017-2019 Lars Wirzenius # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or @@ -16,7 +16,7 @@ from .version import __version__, __version_info__ from .logging import setup_logging, log from .persistent import ( - FilePersistentState, + MemoryPersistentState, NotFound, Resource, resource_from_dict, diff --git a/ick2/apibase.py b/ick2/apibase.py index c08f7cf..f330eae 100644 --- a/ick2/apibase.py +++ b/ick2/apibase.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2018 Lars Wirzenius +# Copyright (C) 2017-2019 Lars Wirzenius # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or @@ -19,7 +19,7 @@ import ick2 class APIbase: def __init__(self, state): - assert state is None or isinstance(state, ick2.FilePersistentState) + assert state is None or isinstance(state, ick2.MemoryPersistentState) self._trans = ick2.TransactionalState(state) def get_routes(self, path): diff --git a/ick2/persistent.py b/ick2/persistent.py index 1d79e3d..bb0aeb6 100644 --- a/ick2/persistent.py +++ b/ick2/persistent.py @@ -49,64 +49,33 @@ class PersistentStateInterface: # pragma: no cover raise NotImplementedError() -class FilePersistentState(PersistentStateInterface): +class MemoryPersistentState(PersistentStateInterface): def __init__(self): - self._dir = None + self._res = {} - def get_directory(self): - return self._dir - - def set_directory(self, dirname): - self._dir = dirname - - def _safe(self, name): - return urllib.parse.quote(name, safe='') - - def _unsafe(self, safe): - return urllib.parse.unquote(safe) - - def _unsafe_list(self, safe_names): - return [self._unsafe(safe) for safe in safe_names] - - def _dirname(self, kind): - return os.path.join(self._dir, self._safe(kind)) - - def _filename(self, kind, rid): - dirname = self._dirname(kind) - return os.path.join(dirname, self._safe(rid)) + def get_resource_ids(self, kind): + if kind not in self._res: + return [] + return list(self._res[kind].keys()) def has_resource(self, kind, rid): - filename = self._filename(kind, rid) - return os.path.exists(filename) - - def get_resource_ids(self, kind): - dirname = self._dirname(kind) - if os.path.exists(dirname): - return self._unsafe_list(os.listdir(dirname)) - return [] + return kind in self._res and rid in self._res[kind] def get_resource(self, kind, rid): - filename = self._filename(kind, rid) - if not os.path.exists(filename): + if kind not in self._res or rid not in self._res[kind]: raise ick2.NotFound(kind=kind, rid=rid) - with open(filename, 'r') as f: - as_dict = yaml.load(f, Loader=yaml.CSafeLoader) - return resource_from_dict(as_dict) + return self._res[kind][rid] def write_resource(self, kind, rid, resource): - dirname = self._dirname(kind) - if not os.path.exists(dirname): - os.makedirs(dirname) - - filename = self._filename(kind, rid) - with open(filename, 'w') as f: - yaml.dump( - resource.as_dict(), stream=f, Dumper=yaml.CSafeDumper) + if kind not in self._res: + self._res[kind] = {} + self._res[kind][rid] = resource def remove_resource(self, kind, rid): - filename = self._filename(kind, rid) - os.remove(filename) + if kind not in self._res or rid not in self._res[kind]: + raise ick2.NotFound(kind=kind, rid=rid) + del self._res[kind][rid] class NotFound(Exception): diff --git a/ick2/persistent_tests.py b/ick2/persistent_tests.py index de279a1..61cd43f 100644 --- a/ick2/persistent_tests.py +++ b/ick2/persistent_tests.py @@ -22,18 +22,7 @@ import unittest import ick2 -class FilePersistentStateTests(unittest.TestCase): - - def setUp(self): - self.tempdir = tempfile.mkdtemp() - self.state = ick2.FilePersistentState() - self.state.set_directory(self.tempdir) - - def tearDown(self): - shutil.rmtree(self.tempdir) - - def test_returns_dirname(self): - self.assertEqual(self.state.get_directory(), self.tempdir) +class PersistentStateTestsMixIn: def test_has_no_resources_initially(self): self.assertEqual(self.state.get_resource_ids('silly'), []) @@ -60,3 +49,20 @@ class FilePersistentStateTests(unittest.TestCase): self.state.remove_resource('silly', '#1') self.assertFalse(self.state.has_resource('silly', '#1')) self.assertEqual(self.state.get_resource_ids('silly'), []) + + def test_raises_error_removing_nonexistent_resource_kind(self): + with self.assertRaises(ick2.NotFound): + self.state.remove_resource('silly', '#1') + + def test_raises_error_removing_nonexistent_resource(self): + as_dict = {'foo': 'bar'} + r = ick2.resource_from_dict(as_dict) + self.state.write_resource('silly', '#1', r) + with self.assertRaises(ick2.NotFound): + self.state.remove_resource('silly', '#2') + + +class MemoryPersistentStateTests(unittest.TestCase, PersistentStateTestsMixIn): + + def setUp(self): + self.state = ick2.MemoryPersistentState() diff --git a/ick2/projectapi_tests.py b/ick2/projectapi_tests.py index b6ec9e9..5be2a58 100644 --- a/ick2/projectapi_tests.py +++ b/ick2/projectapi_tests.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2018 Lars Wirzenius +# Copyright (C) 2017-2019 Lars Wirzenius # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or @@ -27,8 +27,7 @@ class ProjectAPITests(unittest.TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() self.statedir = os.path.join(self.tempdir, 'state/dir') - self.state = ick2.FilePersistentState() - self.state.set_directory(self.statedir) + self.state = ick2.MemoryPersistentState() def tearDown(self): shutil.rmtree(self.tempdir) diff --git a/ick2/workapi_tests.py b/ick2/workapi_tests.py index c368b4b..8652110 100644 --- a/ick2/workapi_tests.py +++ b/ick2/workapi_tests.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2018 Lars Wirzenius +# Copyright (C) 2017-2019 Lars Wirzenius # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or @@ -28,8 +28,7 @@ class WorkAPITests(unittest.TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() self.statedir = os.path.join(self.tempdir, 'state/dir') - self.state = ick2.FilePersistentState() - self.state.set_directory(self.statedir) + self.state = ick2.MemoryPersistentState() self.claims = None def tearDown(self): -- cgit v1.2.1