From 16cfc5e55318d424dc434f931469f7f8a6f29689 Mon Sep 17 00:00:00 2001 From: Martin Curlej Date: Sep 27 2017 19:34:39 +0000 Subject: Added custom types for yaml fields to preserve newlines added test, bugfix improved test --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index a841c71..47a4212 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -57,6 +57,7 @@ from modulemd.components.module import ModuleComponentModule from modulemd.components.rpm import ModuleComponentRPM from modulemd.filter import ModuleFilter from modulemd.profile import ModuleProfile +from modulemd.util import PreservedDesc, PreservedMacros supported_mdversions = ( 1, ) @@ -458,7 +459,28 @@ class ModuleMetadata(object): :rtype: str """ - return yaml.safe_dump(self.dumpd(), default_flow_style=False) + + # we need to extend the macros and description property of the dict + # so we can preserve the correct newlines when dumping the dict to YAML + # PyYAML requires that each of those properties has a unique type so + # they can be identified by the Dumper. + mmd = self.dumpd() + if "buildopts" in mmd["data"]: + macros_string = mmd["data"]["buildopts"]["rpms"]["macros"] + yaml.add_representer(PreservedMacros, PreservedMacros.representer) + pmacros = PreservedMacros(macros_string) + mmd["data"]["buildopts"]["rpms"]["macros"] = pmacros + + if "description" in mmd["data"]: + desc_string = mmd["data"]["description"] + yaml.add_representer(PreservedDesc, PreservedDesc.representer) + pdesc = PreservedDesc(desc_string) + mmd["data"]["description"] = pdesc + + return yaml.dump_all([mmd], + None, + Dumper=yaml.Dumper, + default_flow_style=False) @property def mdversion(self): diff --git a/modulemd/tests/test_io.py b/modulemd/tests/test_io.py index c420eb9..c77ba93 100644 --- a/modulemd/tests/test_io.py +++ b/modulemd/tests/test_io.py @@ -29,6 +29,7 @@ import unittest import os import sys import datetime +import yaml DIR = os.path.dirname(__file__) sys.path.insert(0, os.path.join(DIR, "..")) @@ -149,3 +150,16 @@ class TestIO(unittest.TestCase): self.assertEqual(arepr, repr(a)) self.assertEqual(brepr, repr(b)) os.remove("testdumpall.yaml") + + def test_dumps_correct_num_newlines(self): + mmd = ModuleMetadata() + mmd.buildopts.rpms.macros = "1\n2\n3\n" + mmd.description = "test test test test test test\n" + + data = mmd.dumpd() + bad_yaml_str = yaml.safe_dump(data, default_flow_style=False) + correct_yaml_str = mmd.dumps() + self.assertNotEqual( + bad_yaml_str.count('\n'), + correct_yaml_str.count('\n') + ) diff --git a/modulemd/util.py b/modulemd/util.py new file mode 100644 index 0000000..318a301 --- /dev/null +++ b/modulemd/util.py @@ -0,0 +1,27 @@ + +class PreservedMacros(str): + """ + Extended string type used for preserving newlines in strings used by + PyYAML package when dumping dicts to yaml files. This type defines + the macros field from the YAML metadata. + """ + @staticmethod + def representer(dumper, data): + """ + Representer method which defines the YAML field we want to preserve. + """ + return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|") + + +class PreservedDesc(str): + """ + Extended string type used for preserving newlines in strings used by + PyYAML package when dumping dicts to yaml files. This type defines + the Description field from the YAML metadata. + """ + @staticmethod + def representer(dumper, data): + """ + Representer method which defines the YAML field we want to preserve. + """ + return dumper.represent_scalar("tag:yaml.org,2002:str", data, style=">")