summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2016-06-12 10:51:28 +0300
committerLars Wirzenius <liw@liw.fi>2016-06-12 11:50:06 +0300
commitdcbbc336897b56dde3d25e228858d41bf43523a4 (patch)
treed9cb590c0e311b1f92ddab1488d7adf32a392961
parent8b0b96472073b1ef0e44b93d9a1c11df31491ac9 (diff)
downloadobnam-dcbbc336897b56dde3d25e228858d41bf43523a4.tar.gz
Add a MealieReader class
-rw-r--r--meliaereader/__init__.py19
-rw-r--r--meliaereader/reader.py76
-rw-r--r--meliaereader/reader_tests.py129
-rw-r--r--without-tests2
4 files changed, 226 insertions, 0 deletions
diff --git a/meliaereader/__init__.py b/meliaereader/__init__.py
new file mode 100644
index 00000000..8cf0a0ac
--- /dev/null
+++ b/meliaereader/__init__.py
@@ -0,0 +1,19 @@
+# Copyright 2016 Lars Wirzenius
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# =*= License: GPL-3+ =*=
+
+
+from .reader import MeliaeReader
diff --git a/meliaereader/reader.py b/meliaereader/reader.py
new file mode 100644
index 00000000..a0a2f4eb
--- /dev/null
+++ b/meliaereader/reader.py
@@ -0,0 +1,76 @@
+# Copyright 2016 Lars Wirzenius
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# =*= License: GPL-3+ =*=
+
+
+import json
+
+
+class MeliaeReader(object):
+
+ def __init__(self):
+ self._objs = []
+
+ def __iter__(self):
+ return iter(self._objs)
+
+ def __contains__(self, ref):
+ return any(o['address'] == ref for o in self)
+
+ def __len__(self):
+ return len(self._objs)
+
+ def read(self, filename):
+ with open(filename) as f:
+ self._objs.extend(json.loads(line) for line in f)
+
+ def get_total_size(self):
+ return self.get_size(self._objs)
+
+ def get_size(self, objs):
+ return sum(o['size'] for o in objs)
+
+ def get_types(self):
+ return set(o['type'] for o in self)
+
+ def get_objs_of_type(self, typename):
+ return [o for o in self if o['type'] == typename]
+
+ def get_closure(self, obj):
+ closure = []
+ todo = [obj]
+ done = set()
+ while todo:
+ o = todo.pop(0)
+ done.add(o['address'])
+ closure.append(o)
+ for ref in o['refs']:
+ if ref not in done and ref in self:
+ todo.append(self.get_object(ref))
+ return closure
+
+ def get_object(self, ref):
+ for obj in self:
+ if obj['address'] == ref:
+ return obj
+ raise Exception('No object with address {}'.format(ref))
+
+ def get_closure_of_type(self, typename):
+ type_closure = {}
+ for obj in self.get_objs_of_type(typename):
+ for o in self.get_closure(obj):
+ type_closure[o['address']] = o
+ return type_closure.values()
diff --git a/meliaereader/reader_tests.py b/meliaereader/reader_tests.py
new file mode 100644
index 00000000..c50e97c1
--- /dev/null
+++ b/meliaereader/reader_tests.py
@@ -0,0 +1,129 @@
+# Copyright 2016 Lars Wirzenius
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# =*= License: GPL-3+ =*=
+
+
+import json
+import os
+import shutil
+import tempfile
+import unittest
+
+import meliaereader
+
+
+class MeliaeReaderTests(unittest.TestCase):
+
+ def setUp(self):
+ self.tempdir = tempfile.mkdtemp()
+
+ def tearDown(self):
+ shutil.rmtree(self.tempdir)
+
+ def make_file(self, *objs):
+ fd, filename = tempfile.mkstemp(dir=self.tempdir)
+ os.close(fd)
+ with open(filename, 'w') as f:
+ for obj in objs:
+ f.write('{}\n'.format(json.dumps(obj)))
+ return filename
+
+ def make_object(self, **kwargs):
+ return kwargs
+
+ def test_has_no_objects_initially(self):
+ mr = meliaereader.MeliaeReader()
+ self.assertEqual(len(mr), 0)
+
+ def test_reads_an_empty_file(self):
+ mr = meliaereader.MeliaeReader()
+ mr.read('/dev/null')
+ self.assertEqual(len(mr), 0)
+
+ def test_reads_file_with_one_object(self):
+ filename = self.make_file(self.make_object())
+ mr = meliaereader.MeliaeReader()
+ mr.read(filename)
+ self.assertEqual(len(mr), 1)
+
+ def test_get_object_raises_exception_if_not_found(self):
+ mr = meliaereader.MeliaeReader()
+ self.assertRaises(Exception, mr.get_object, 1)
+
+ def test_get_object_returns_object_if_found(self):
+ obj = self.make_object(type='foo', address=1)
+ filename = self.make_file(obj)
+ mr = meliaereader.MeliaeReader()
+ mr.read(filename)
+ self.assertEqual(mr.get_object(obj['address']), obj)
+
+ def test_reports_total_size_correctly(self):
+ obj = self.make_object(type='foo', address=1, size=42)
+ filename = self.make_file(obj)
+ mr = meliaereader.MeliaeReader()
+ mr.read(filename)
+ self.assertEqual(mr.get_total_size(), 42)
+
+ def test_report_size_for_list_of_objects_correctly(self):
+ obj = self.make_object(type='foo', address=1, size=42)
+ filename = self.make_file(obj)
+ mr = meliaereader.MeliaeReader()
+ mr.read(filename)
+ self.assertEqual(mr.get_size([obj]), 42)
+
+ def test_readings_adds_to_existing_objs(self):
+ filename_1 = self.make_file(self.make_object())
+ filename_2 = self.make_file(self.make_object())
+ mr = meliaereader.MeliaeReader()
+ mr.read(filename_1)
+ mr.read(filename_2)
+ self.assertEqual(len(mr), 2)
+
+ def test_reports_object_types(self):
+ filename = self.make_file(self.make_object(type='foo'))
+ mr = meliaereader.MeliaeReader()
+ mr.read(filename)
+ self.assertEqual(mr.get_types(), set(['foo']))
+
+ def test_reports_objs_of_type(self):
+ obj_1 = self.make_object(type='foo')
+ obj_2 = self.make_object(type='bar')
+ filename = self.make_file(obj_1, obj_2)
+ mr = meliaereader.MeliaeReader()
+ mr.read(filename)
+ self.assertEqual(sorted(mr.get_types()), ['bar', 'foo'])
+ self.assertEqual(mr.get_objs_of_type('foo'), [obj_1])
+ self.assertEqual(mr.get_objs_of_type('bar'), [obj_2])
+ self.assertEqual(mr.get_objs_of_type('nonononono'), [])
+
+ def test_reports_closure_for_object(self):
+ obj_1 = self.make_object(type='foo', address=1, refs=[2])
+ obj_2 = self.make_object(type='bar', address=2, refs=[])
+ filename = self.make_file(obj_1, obj_2)
+ mr = meliaereader.MeliaeReader()
+ mr.read(filename)
+ self.assertEqual(mr.get_closure(obj_1), [obj_1, obj_2])
+ self.assertEqual(mr.get_closure(obj_2), [obj_2])
+
+ def test_reports_closure_for_type(self):
+ obj_1 = self.make_object(type='foo', address=1, refs=[2])
+ obj_2 = self.make_object(type='bar', address=2, refs=[])
+ filename = self.make_file(obj_1, obj_2)
+ mr = meliaereader.MeliaeReader()
+ mr.read(filename)
+ self.assertEqual(list(mr), [obj_1, obj_2])
+ self.assertEqual(mr.get_closure_of_type('foo'), [obj_1, obj_2])
+ self.assertEqual(mr.get_closure_of_type('bar'), [obj_2])
diff --git a/without-tests b/without-tests
index fc8b281c..66df6b0d 100644
--- a/without-tests
+++ b/without-tests
@@ -1,5 +1,7 @@
setup.py
+meliaereader/__init__.py
+
obnamlib/app.py
obnamlib/backup_progress.py
obnamlib/defaults.py