summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--morphlib/__init__.py3
-rw-r--r--morphlib/morph2.py26
-rw-r--r--morphlib/morphologyfactory.py6
-rw-r--r--morphlib/yamlparse.py119
-rwxr-xr-xrun-bootstrap-in-chroot2
5 files changed, 147 insertions, 9 deletions
diff --git a/morphlib/__init__.py b/morphlib/__init__.py
index 213241d8..5730d41e 100644
--- a/morphlib/__init__.py
+++ b/morphlib/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2011-2012 Codethink Limited
+# Copyright (C) 2011-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
@@ -56,5 +56,6 @@ import stagingarea
import stopwatch
import tempdir
import util
+import yamlparse
import app # this needs to be last
diff --git a/morphlib/morph2.py b/morphlib/morph2.py
index 9e5be2e6..edf7bb31 100644
--- a/morphlib/morph2.py
+++ b/morphlib/morph2.py
@@ -17,6 +17,7 @@
import copy
import re
+import morphlib
from morphlib.util import OrderedDict, json
class Morphology(object):
@@ -52,8 +53,26 @@ class Morphology(object):
]
}
+ @staticmethod
+ def _load_json(text):
+ return json.loads(text, object_pairs_hook=OrderedDict)
+
+ @staticmethod
+ def _dump_json(obj, f):
+ text = json.dumps(obj, indent=4)
+ text = re.sub(" \n", "\n", text)
+ f.write(text)
+ f.write('\n')
+
def __init__(self, text):
- self._dict = json.loads(text, object_pairs_hook=OrderedDict)
+ # Load as JSON first, then try YAML, so morphologies
+ # that read as JSON are dumped as JSON, likewise with YAML.
+ try:
+ self._dict = self._load_json(text)
+ self._dumper = self._dump_json
+ except Exception, e:
+ self._dict = morphlib.yamlparse.load(text)
+ self._dumper = morphlib.yamlparse.dump
self._set_defaults()
self._validate_children()
@@ -156,7 +175,4 @@ class Morphology(object):
value = self[key]
if value and key[0] != '_':
as_dict[key] = value
- text = json.dumps(as_dict, indent=4)
- text = re.sub(" \n", "\n", text)
- f.write(text)
- f.write('\n')
+ self._dumper(as_dict, f)
diff --git a/morphlib/morphologyfactory.py b/morphlib/morphologyfactory.py
index a219ed9b..261dc908 100644
--- a/morphlib/morphologyfactory.py
+++ b/morphlib/morphologyfactory.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 Codethink Limited
+# Copyright (C) 2012-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
@@ -13,6 +13,8 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+import yaml
+
import morphlib
import cliapp
@@ -81,7 +83,7 @@ class MorphologyFactory(object):
try:
morphology = morphlib.morph2.Morphology(text)
- except ValueError as e:
+ except yaml.YAMLError as e:
raise morphlib.Error("Error parsing %s: %s" %
(filename, str(e)))
diff --git a/morphlib/yamlparse.py b/morphlib/yamlparse.py
new file mode 100644
index 00000000..7f8b00e5
--- /dev/null
+++ b/morphlib/yamlparse.py
@@ -0,0 +1,119 @@
+# 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.
+
+import yaml
+import yaml.constructor
+
+from morphlib.util import OrderedDict
+
+class OrderedDictYAMLLoader(yaml.Loader):
+ """A YAML loader that loads mappings into ordered dictionaries.
+
+ When YAML is loaded with this Loader, it loads mappings as ordered
+ dictionaries, so the order the keys were written in is maintained.
+
+ When combined with the OrderedDictYAMLDumper, this allows yaml documents
+ to be written out in a similar format to they were read.
+
+ """
+
+ def __init__(self, *args, **kwargs):
+ yaml.Loader.__init__(self, *args, **kwargs)
+
+ # When YAML encounters a mapping (which YAML identifies with
+ # the given tag), it will use construct_yaml_map to read it as
+ # an OrderedDict.
+ self.add_constructor(u'tag:yaml.org,2002:map',
+ type(self).construct_yaml_map)
+
+ def construct_yaml_map(self, node):
+ data = OrderedDict()
+ yield data
+ value = self.construct_mapping(node)
+ data.update(value)
+
+ def construct_mapping(self, node, deep=False):
+ if isinstance(node, yaml.MappingNode):
+ self.flatten_mapping(node)
+ else:
+ raise yaml.constructor.ConstructorError(
+ None, None,
+ 'expected a mapping node, but found %s' % node.id,
+ node.start_mark)
+
+ mapping = OrderedDict()
+ for key_node, value_node in node.value:
+ key = self.construct_object(key_node, deep=deep)
+ try:
+ hash(key)
+ except TypeError, exc:
+ raise yaml.constructor.ConstructorError(
+ 'while constructing a mapping', node.start_mark,
+ 'found unacceptable key (%s)' % exc, key_node.start_mark)
+ value = self.construct_object(value_node, deep=deep)
+ mapping[key] = value
+ return mapping
+
+class OrderedDictYAMLDumper(yaml.Dumper):
+ """A YAML dumper that will dump OrderedDicts as mappings.
+
+ When YAML is dumped with this Dumper, it dumps OrderedDicts as
+ mappings, preserving the key order, so the order the keys were
+ written in is maintained.
+
+ When combined with the OrderedDictYAMLDumper, this allows yaml documents
+ to be written out in a similar format to they were read.
+
+ """
+
+ def __init__(self, *args, **kwargs):
+ yaml.Dumper.__init__(self, *args, **kwargs)
+
+ # When YAML sees an OrderedDict, use represent_ordered_dict to dump it
+ self.add_representer(OrderedDict,
+ type(self).represent_ordered_dict)
+
+ def represent_ordered_dict(self, odict):
+ return self.represent_ordered_mapping(u'tag:yaml.org,2002:map', odict)
+
+ def represent_ordered_mapping(self, tag, omap):
+ value = []
+ node = yaml.MappingNode(tag, value)
+ if self.alias_key is not None:
+ self.represented_objects[self.alias_key] = node
+ best_style = True
+ for item_key, item_value in omap.iteritems():
+ node_key = self.represent_data(item_key)
+ node_value = self.represent_data(item_value)
+ if not (isinstance(node_key, yaml.ScalarNode) and
+ not node_key.style):
+ best_style = False # pragma: no cover
+ if not (isinstance(node_value, yaml.ScalarNode) and
+ not node_value.style):
+ best_style = False # pragma: no cover
+ value.append((node_key, node_value))
+ if self.default_flow_style is not None:
+ node.flow_style = self.default_flow_style
+ else:
+ node.flow_style = best_style # pragma: no cover
+ return node
+
+def load(*args, **kwargs):
+ return yaml.load(Loader=OrderedDictYAMLLoader, *args, **kwargs)
+
+def dump(*args, **kwargs):
+ if 'default_flow_style' not in kwargs:
+ kwargs['default_flow_style'] = False
+ return yaml.dump(Dumper=OrderedDictYAMLDumper, *args, **kwargs)
diff --git a/run-bootstrap-in-chroot b/run-bootstrap-in-chroot
index 20a67c5a..fbe1e9f5 100755
--- a/run-bootstrap-in-chroot
+++ b/run-bootstrap-in-chroot
@@ -128,7 +128,7 @@ if ! "$snapshot" || ! has_pass pass1a; then
EXTRAPACKAGES="build-essential,gawk,bison,flex,python,autoconf"
EXTRAPACKAGES="$EXTRAPACKAGES,autopoint,automake,gettext,libtool"
EXTRAPACKAGES="$EXTRAPACKAGES,help2man,texinfo,sudo,ccache,gperf"
- EXTRAPACKAGES="$EXTRAPACKAGES,python-pip,python-simplejson"
+ EXTRAPACKAGES="$EXTRAPACKAGES,python-pip,python-simplejson,python-yaml"
EXTRAARGS=
if [ -n $ARCH]; then