diff options
author | Lars Wirzenius <liw@liw.fi> | 2016-06-12 10:51:28 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2016-06-12 11:50:06 +0300 |
commit | dcbbc336897b56dde3d25e228858d41bf43523a4 (patch) | |
tree | d9cb590c0e311b1f92ddab1488d7adf32a392961 | |
parent | 8b0b96472073b1ef0e44b93d9a1c11df31491ac9 (diff) | |
download | obnam-dcbbc336897b56dde3d25e228858d41bf43523a4.tar.gz |
Add a MealieReader class
-rw-r--r-- | meliaereader/__init__.py | 19 | ||||
-rw-r--r-- | meliaereader/reader.py | 76 | ||||
-rw-r--r-- | meliaereader/reader_tests.py | 129 | ||||
-rw-r--r-- | without-tests | 2 |
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 |