_yaml.pyx: port roundtripping code to the new ruamel.yaml API
Also drop roundtrip_load_data that was unused
Fixes #1495
diff --git a/src/buildstream/_yaml.pyx b/src/buildstream/_yaml.pyx
index 64b76c5..a07353d 100644
--- a/src/buildstream/_yaml.pyx
+++ b/src/buildstream/_yaml.pyx
@@ -378,25 +378,25 @@
 yaml.RoundTripConstructor.add_constructor(u'tag:yaml.org,2002:timestamp',
                                           yaml.RoundTripConstructor.construct_yaml_str)
 
-
-# HardlineDumper
-#
 # This is a dumper used during roundtrip_dump which forces every scalar to be
 # a plain string, in order to match the output format to the input format.
 #
 # If you discover something is broken, please add a test case to the roundtrip
 # test in tests/internals/yaml/roundtrip-test.yaml
-#
-class HardlineDumper(yaml.RoundTripDumper):
-    def __init__(self, *args, **kwargs):
-        yaml.RoundTripDumper.__init__(self, *args, **kwargs)
-        # For each of YAML 1.1 and 1.2, force everything to be a plain string
-        for version in [(1, 1), (1, 2), None]:
-            self.add_version_implicit_resolver(
-                version,
-                u'tag:yaml.org,2002:str',
-                yaml.util.RegExp(r'.*'),
-                None)
+def prepare_roundtrip_yaml():
+    yml = yaml.YAML()
+    yml.preserve_quotes=True
+
+    # For each of YAML 1.1 and 1.2, force everything to be a plain string
+
+    for version in [(1, 1), (1, 2), None]:
+        yml.resolver.add_version_implicit_resolver(
+            version,
+            u'tag:yaml.org,2002:str',
+            yaml.util.RegExp(r'.*'),
+            None)
+
+    return yml
 
 
 # roundtrip_load()
@@ -420,10 +420,23 @@
 #                 Also if the YAML is malformed.
 #
 def roundtrip_load(filename, *, allow_missing=False):
+    yml = prepare_roundtrip_yaml()
     try:
         with open(filename, "r") as fh:
-            data = fh.read()
-        contents = roundtrip_load_data(data, filename=filename)
+            try:
+                contents = yml.load(fh)
+            except (yaml.scanner.ScannerError, yaml.composer.ComposerError, yaml.parser.ParserError) as e:
+                raise LoadError("Malformed YAML:\n\n{}\n\n{}\n".format(e.problem, e.problem_mark),
+                                LoadErrorReason.INVALID_YAML) from e
+
+            # Special case empty files at this point
+            if contents is None:
+                # We'll make them empty mappings like the main Node loader
+                contents = {}
+
+            if not isinstance(contents, Mapping):
+                raise LoadError("YAML file has content of type '{}' instead of expected type 'dict': {}"
+                                .format(type(contents).__name__, filename), LoadErrorReason.INVALID_YAML)
     except FileNotFoundError as e:
         if allow_missing:
             # Missing files are always empty dictionaries
@@ -437,43 +450,6 @@
     return contents
 
 
-# roundtrip_load_data()
-#
-# Parse the given contents as YAML, returning them as a roundtrippable data
-# structure.
-#
-# A lack of content will be returned as an empty mapping.
-#
-# Args:
-#    contents (str): The contents to be parsed as YAML
-#    filename (str): Optional filename to be used in error reports
-#
-# Returns:
-#    (Mapping): The loaded YAML mapping
-#
-# Raises:
-#    (LoadError): Raised on invalid YAML, or YAML which parses to something other
-#                 than a Mapping
-#
-def roundtrip_load_data(contents, *, filename=None):
-    try:
-        contents = yaml.load(contents, yaml.RoundTripLoader, preserve_quotes=True)
-    except (yaml.scanner.ScannerError, yaml.composer.ComposerError, yaml.parser.ParserError) as e:
-        raise LoadError("Malformed YAML:\n\n{}\n\n{}\n".format(e.problem, e.problem_mark),
-                        LoadErrorReason.INVALID_YAML) from e
-
-    # Special case empty files at this point
-    if contents is None:
-        # We'll make them empty mappings like the main Node loader
-        contents = {}
-
-    if not isinstance(contents, Mapping):
-        raise LoadError("YAML file has content of type '{}' instead of expected type 'dict': {}"
-                        .format(type(contents).__name__, filename), LoadErrorReason.INVALID_YAML)
-
-    return contents
-
-
 # roundtrip_dump()
 #
 # Dumps the given contents as a YAML file.  Ideally the contents came from
@@ -488,6 +464,7 @@
 #    file (any): The file to write to
 #
 def roundtrip_dump(contents, file=None):
+    yml = prepare_roundtrip_yaml()
     with ExitStack() as stack:
         if type(file) is str:
             from . import utils
@@ -496,4 +473,4 @@
             f = file
         else:
             f = sys.stdout
-        yaml.round_trip_dump(contents, f, Dumper=HardlineDumper)
+        yml.dump(contents, f)
diff --git a/tests/internals/yaml.py b/tests/internals/yaml.py
index 452906f..ee5ad94 100644
--- a/tests/internals/yaml.py
+++ b/tests/internals/yaml.py
@@ -400,21 +400,13 @@
     assert exc.value.reason == LoadErrorReason.INVALID_DATA
 
 
-# This test has been broken by upstream ruamel.yaml, filed an issue here:
-#
-#    https://sourceforge.net/p/ruamel-yaml/tickets/390/
-#
-@pytest.mark.xfail(reason="recent versions of ruamel.yaml have broken roundtripping perfection")
 @pytest.mark.datafiles(os.path.join(DATA_DIR))
-@pytest.mark.parametrize("fromdisk", [(True), (False)])
-def test_roundtrip_dump(datafiles, fromdisk):
+def test_roundtrip_dump(datafiles):
     filename = os.path.join(datafiles.dirname, datafiles.basename, "roundtrip-test.yaml")
     with open(filename, "r", encoding="utf-8") as fh:
         rt_raw = fh.read()
-    if fromdisk:
-        rt_loaded = _yaml.roundtrip_load(filename)
-    else:
-        rt_loaded = _yaml.roundtrip_load_data(rt_raw, filename=filename)
+
+    rt_loaded = _yaml.roundtrip_load(filename)
 
     # Now walk the loaded data structure, checking for ints etc.
     def walk_node(node):