From 22d3fe2832cf758b1efa75d2fdecf6e061fbdb0d Mon Sep 17 00:00:00 2001 From: Richard Maw Date: Mon, 17 Feb 2014 15:01:24 +0000 Subject: morphloader: Dump dicts with a nicer key order --- morphlib/morphloader.py | 58 ++++++++++++++++++++++++++++++++++++++++++- morphlib/morphloader_tests.py | 40 ++++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 5 deletions(-) (limited to 'morphlib') diff --git a/morphlib/morphloader.py b/morphlib/morphloader.py index e4367fa1..cd005fae 100644 --- a/morphlib/morphloader.py +++ b/morphlib/morphloader.py @@ -205,6 +205,61 @@ class MultipleValidationErrors(morphlib.Error): self.msg += ('\t' + str(error)) +class OrderedDumper(yaml.SafeDumper): + keyorder = ( + 'name', + 'kind', + 'description', + 'arch', + 'strata', + 'configuration-extensions', + 'morph', + 'repo', + 'ref', + 'unpetrify-ref', + 'build-depends', + 'build-mode', + 'artifacts', + 'max-jobs', + 'products', + 'chunks', + 'build-system', + 'pre-configure-commands', + 'configure-commands', + 'post-configure-commands', + 'pre-build-commands', + 'build-commands', + 'post-build-commands', + 'pre-install-commands', + 'install-commands', + 'post-install-commands', + 'artifact', + 'include', + 'systems', + 'deploy', + 'type', + 'location', + ) + + @classmethod + def _iter_in_global_order(cls, mapping): + for key in cls.keyorder: + if key in mapping: + yield key, mapping[key] + for key in sorted(mapping.iterkeys()): + if key not in cls.keyorder: + yield key, mapping[key] + + @classmethod + def _represent_dict(cls, dumper, mapping): + return dumper.represent_mapping('tag:yaml.org,2002:map', + cls._iter_in_global_order(mapping)) + + def __init__(self, *args, **kwargs): + yaml.SafeDumper.__init__(self, *args, **kwargs) + self.add_representer(dict, self._represent_dict) + + class MorphologyLoader(object): '''Load morphologies from disk, or save them back to disk.''' @@ -324,7 +379,8 @@ class MorphologyLoader(object): def save_to_string(self, morphology): '''Return normalised textual form of morphology.''' - return yaml.safe_dump(morphology.data, default_flow_style=False) + return yaml.dump(morphology.data, Dumper=OrderedDumper, + default_flow_style=False) def save_to_file(self, filename, morphology): '''Save a morphology object to a named file.''' diff --git a/morphlib/morphloader_tests.py b/morphlib/morphloader_tests.py index b8738804..a050e10b 100644 --- a/morphlib/morphloader_tests.py +++ b/morphlib/morphloader_tests.py @@ -505,9 +505,9 @@ build-system: dummy # The following verifies that the YAML is written in a normalised # fashion. self.assertEqual(text, '''\ -build-system: dummy -kind: chunk name: foo +kind: chunk +build-system: dummy ''') def test_saves_to_file(self): @@ -524,9 +524,9 @@ name: foo # The following verifies that the YAML is written in a normalised # fashion. self.assertEqual(text, '''\ -build-system: dummy -kind: chunk name: foo +kind: chunk +build-system: dummy ''') def test_validate_does_not_set_defaults(self): @@ -862,3 +862,35 @@ name: foo self.assertEqual(warning.morphology_name, 'foo') self.assertEqual(warning.stratum_name, 'bar') self.assertEqual(warning.field, obsolete_field) + + def test_unordered_asciibetically_after_ordered(self): + # We only get morphologies with arbitrary keys in clusters + m = morphlib.morph3.Morphology( + name='foo', + kind='cluster', + systems=[ + { + 'morph': 'system-name', + 'repo': 'test:morphs', + 'ref': 'master', + 'deploy': { + 'deployment-foo': { + 'type': 'tarball', + 'location': '/tmp/path.tar', + 'HOSTNAME': 'aasdf', + } + } + } + ] + ) + s = self.loader.save_to_string(m) + # root field order + self.assertLess(s.find('name'), s.find('kind')) + self.assertLess(s.find('kind'), s.find('systems')) + # systems field order + self.assertLess(s.find('morph'), s.find('repo')) + self.assertLess(s.find('repo'), s.find('ref')) + self.assertLess(s.find('ref'), s.find('deploy')) + # deployment keys field order + self.assertLess(s.find('type'), s.find('location')) + self.assertLess(s.find('location'), s.find('HOSTNAME')) -- cgit v1.2.1