From 7e674fb46c1e42506c15b3a79c120956b3ff3336 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Fri, 18 Oct 2019 11:39:28 +0300 Subject: Revert "Add: MemoryPersitentState" This reverts commit 581f6cd53f57599326439dccb355e882e7858faa. --- 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, 68 insertions(+), 41 deletions(-) diff --git a/ick2/__init__.py b/ick2/__init__.py index 0016f13..f489396 100644 --- a/ick2/__init__.py +++ b/ick2/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2019 Lars Wirzenius +# Copyright (C) 2017-2018 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 ( - MemoryPersistentState, + FilePersistentState, NotFound, Resource, resource_from_dict, diff --git a/ick2/apibase.py b/ick2/apibase.py index f330eae..c08f7cf 100644 --- a/ick2/apibase.py +++ b/ick2/apibase.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2019 Lars Wirzenius +# Copyright (C) 2017-2018 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.MemoryPersistentState) + assert state is None or isinstance(state, ick2.FilePersistentState) self._trans = ick2.TransactionalState(state) def get_routes(self, path): diff --git a/ick2/persistent.py b/ick2/persistent.py index bb0aeb6..1d79e3d 100644 --- a/ick2/persistent.py +++ b/ick2/persistent.py @@ -49,33 +49,64 @@ class PersistentStateInterface: # pragma: no cover raise NotImplementedError() -class MemoryPersistentState(PersistentStateInterface): +class FilePersistentState(PersistentStateInterface): def __init__(self): - self._res = {} + self._dir = None - def get_resource_ids(self, kind): - if kind not in self._res: - return [] - return list(self._res[kind].keys()) + 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 has_resource(self, kind, rid): - return kind in self._res and rid in self._res[kind] + 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 [] def get_resource(self, kind, rid): - if kind not in self._res or rid not in self._res[kind]: + filename = self._filename(kind, rid) + if not os.path.exists(filename): raise ick2.NotFound(kind=kind, rid=rid) - return self._res[kind][rid] + with open(filename, 'r') as f: + as_dict = yaml.load(f, Loader=yaml.CSafeLoader) + return resource_from_dict(as_dict) def write_resource(self, kind, rid, resource): - if kind not in self._res: - self._res[kind] = {} - self._res[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) def remove_resource(self, kind, rid): - 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] + filename = self._filename(kind, rid) + os.remove(filename) class NotFound(Exception): diff --git a/ick2/persistent_tests.py b/ick2/persistent_tests.py index 61cd43f..de279a1 100644 --- a/ick2/persistent_tests.py +++ b/ick2/persistent_tests.py @@ -22,7 +22,18 @@ import unittest import ick2 -class PersistentStateTestsMixIn: +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) def test_has_no_resources_initially(self): self.assertEqual(self.state.get_resource_ids('silly'), []) @@ -49,20 +60,3 @@ class PersistentStateTestsMixIn: 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 5be2a58..b6ec9e9 100644 --- a/ick2/projectapi_tests.py +++ b/ick2/projectapi_tests.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2019 Lars Wirzenius +# Copyright (C) 2017-2018 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,7 +27,8 @@ class ProjectAPITests(unittest.TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() self.statedir = os.path.join(self.tempdir, 'state/dir') - self.state = ick2.MemoryPersistentState() + self.state = ick2.FilePersistentState() + self.state.set_directory(self.statedir) def tearDown(self): shutil.rmtree(self.tempdir) diff --git a/ick2/workapi_tests.py b/ick2/workapi_tests.py index 8652110..c368b4b 100644 --- a/ick2/workapi_tests.py +++ b/ick2/workapi_tests.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017-2019 Lars Wirzenius +# Copyright (C) 2017-2018 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,7 +28,8 @@ class WorkAPITests(unittest.TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() self.statedir = os.path.join(self.tempdir, 'state/dir') - self.state = ick2.MemoryPersistentState() + self.state = ick2.FilePersistentState() + self.state.set_directory(self.statedir) self.claims = None def tearDown(self): -- cgit v1.2.1