summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--morphlib/__init__.py1
-rw-r--r--morphlib/systemmetadatadir.py87
-rw-r--r--morphlib/systemmetadatadir_tests.py75
3 files changed, 163 insertions, 0 deletions
diff --git a/morphlib/__init__.py b/morphlib/__init__.py
index b1e3c7c3..7eb3f975 100644
--- a/morphlib/__init__.py
+++ b/morphlib/__init__.py
@@ -78,6 +78,7 @@ import sourcepool
import stagingarea
import stopwatch
import sysbranchdir
+import systemmetadatadir
import tempdir
import util
import workspace
diff --git a/morphlib/systemmetadatadir.py b/morphlib/systemmetadatadir.py
new file mode 100644
index 00000000..eac5b446
--- /dev/null
+++ b/morphlib/systemmetadatadir.py
@@ -0,0 +1,87 @@
+# Copyright (C) 2013 Codethink Limited
+#
+# 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; version 2 of the License.
+#
+# 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, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# =*= License: GPL-2 =*=
+
+
+import collections
+import glob
+import json
+import os
+
+
+class SystemMetadataDir(collections.MutableMapping):
+
+ '''An abstraction over the /baserock metadata directory.
+
+ This allows methods of iterating over it, and accessing it like
+ a dict.
+
+ The /baserock metadata directory contains information about all of
+ the chunks in a built system. It exists to provide traceability from
+ the input sources to the output.
+
+ If you create the object with smd = SystemMetadataDir('/baserock')
+ data = smd['key'] will read /baserock/key.meta and return its JSON
+ encoded contents as native python objects.
+
+ smd['key'] = data will write data to /baserock/key.meta as JSON
+
+ The key may not have '\0' characters in it since the underlying
+ system calls don't support embedded NUL bytes.
+
+ The key may not have '/' characters in it since we do not support
+ morphologies with slashes in their names.
+
+ '''
+
+ def __init__(self, metadata_path):
+ collections.MutableMapping.__init__(self)
+ self._metadata_path = metadata_path
+
+ def _join_path(self, *args):
+ return os.path.join(self._metadata_path, *args)
+
+ def _raw_path_iter(self):
+ return glob.iglob(self._join_path('*.meta'))
+
+ @staticmethod
+ def _check_key(key):
+ if any(c in key for c in "\0/"):
+ raise KeyError(key)
+
+ def __getitem__(self, key):
+ self._check_key(key)
+ try:
+ with open(self._join_path('%s.meta' % key), 'r') as f:
+ return json.load(f)
+ except IOError:
+ raise KeyError(key)
+
+ def __setitem__(self, key, value):
+ self._check_key(key)
+ with open(self._join_path('%s.meta' % key), 'w') as f:
+ json.dump(value, f, indent=4, sort_keys=True)
+
+ def __delitem__(self, key):
+ self._check_key(key)
+ os.unlink(self._join_path('%s.meta' % key))
+
+ def __iter__(self):
+ return (os.path.basename(fn)[:-len('.meta')]
+ for fn in self._raw_path_iter())
+
+ def __len__(self):
+ return len(list(self._raw_path_iter()))
diff --git a/morphlib/systemmetadatadir_tests.py b/morphlib/systemmetadatadir_tests.py
new file mode 100644
index 00000000..0126f862
--- /dev/null
+++ b/morphlib/systemmetadatadir_tests.py
@@ -0,0 +1,75 @@
+# Copyright (C) 2013 Codethink Limited
+#
+# 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; version 2 of the License.
+#
+# 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, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# =*= License: GPL-2 =*=
+
+
+import operator
+import os
+import shutil
+import tempfile
+import unittest
+
+import morphlib
+
+
+class SystemMetadataDirTests(unittest.TestCase):
+
+ def setUp(self):
+ self.tempdir = tempfile.mkdtemp()
+ self.metadatadir = os.path.join(self.tempdir, 'baserock')
+ os.mkdir(self.metadatadir)
+ self.smd = morphlib.systemmetadatadir.SystemMetadataDir(
+ self.metadatadir)
+
+ def tearDown(self):
+ shutil.rmtree(self.tempdir)
+
+ def test_add_new(self):
+ self.smd['key'] = {'foo': 'bar'}
+ self.assertEqual(self.smd['key']['foo'], 'bar')
+
+ def test_replace(self):
+ self.smd['key'] = {'foo': 'bar'}
+ self.smd['key'] = {'foo': 'baz'}
+ self.assertEqual(self.smd['key']['foo'], 'baz')
+
+ def test_remove(self):
+ self.smd['key'] = {'foo': 'bar'}
+ del self.smd['key']
+ self.assertTrue('key' not in self.smd)
+
+ def test_iterate(self):
+ self.smd['build-essential'] = "Some data"
+ self.smd['core'] = "More data"
+ self.smd['foundation'] = "Yet more data"
+ self.assertEqual(sorted(self.smd.keys()),
+ ['build-essential', 'core', 'foundation'])
+ self.assertEqual(dict(self.smd.iteritems()),
+ {
+ 'build-essential': "Some data",
+ 'core': "More data",
+ 'foundation': "Yet more data",
+ })
+
+ def test_raises_KeyError(self):
+ self.assertRaises(KeyError, operator.getitem, self.smd, 'key')
+
+ def test_validates_keys(self):
+ for key in ('foo/bar', 'baz\0quux'):
+ self.assertRaises(KeyError, operator.getitem, self.smd, key)
+ self.assertRaises(KeyError, operator.setitem,
+ self.smd, key, 'value')
+ self.assertRaises(KeyError, operator.delitem, self.smd, key)