osbuild-mpp: Support loading yaml as well as json files

yaml files are essentially compatible with json files, although
they have some advantages, like allowing comments and being easier
for humans to read/write.

This changes the reading of the file to use a yaml parser instead of
a json parser, but still produces json at the end. I tried manually
converting a json file to yaml and running osbuild-mpp, and it produced
an identical file.

By default the yaml parser doesn't respect order so i had to tweak
the loader a bit to use OrderedDict.
This commit is contained in:
Alexander Larsson 2021-11-18 16:02:59 +01:00 committed by Christian Kellner
parent 04423e8a6a
commit 52c3cb12ba

View file

@ -242,6 +242,7 @@ import collections
import contextlib
import hashlib
import json
import yaml
import os
import pathlib
import string
@ -257,6 +258,33 @@ import hawkey
from osbuild.util.rhsm import Subscriptions
class YamlOrderedLoader(yaml.Loader):
def construct_mapping(self, node, deep=False):
if not isinstance(node, yaml.MappingNode):
raise ConstructorError(None, None,
"expected a mapping node, but found %s" % node.id,
node.start_mark)
mapping = collections.OrderedDict()
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=deep)
if not isinstance(key, collections.abc.Hashable):
raise ConstructorError("while constructing a mapping", node.start_mark,
"found unhashable key", key_node.start_mark)
value = self.construct_object(value_node, deep=deep)
mapping[key] = value
return mapping
def construct_yaml_map(self, node):
data = collections.OrderedDict()
yield data
value = self.construct_mapping(node)
data.update(value)
yaml.add_constructor('tag:yaml.org,2002:map', YamlOrderedLoader.construct_yaml_map)
def yaml_load_ordered(source):
return yaml.load(source, YamlOrderedLoader)
def json_load_ordered(source):
return json.load(source, object_pairs_hook=collections.OrderedDict)
def element_enter(element, key, default):
if key not in element:
@ -603,11 +631,22 @@ class ManifestFile:
@staticmethod
def load_from_fd(f, path, overrides, default_vars):
# We use OrderedDict to preserve key order (for python < 3.6)
try:
data = json.load(f, object_pairs_hook=collections.OrderedDict)
except json.decoder.JSONDecodeError as err:
print (f"Invalid json in \"{path}\": {err.msg} at line {err.lineno} (col {err.colno})")
sys.exit(1)
if path.endswith(".yml") or path.endswith(".yaml"):
try:
data = yaml_load_ordered(f)
except yaml.YAMLError as err:
pos = ""
if hasattr(err, 'problem_mark'):
mark = err.problem_mark
pos = f" at line {mark.line+1} (col {mark.column+1})"
print(f"Invalid yaml in \"{path}\": {err.problem}{pos}")
sys.exit(1)
else:
try:
data = json_load_ordered(f)
except json.decoder.JSONDecodeError as err:
print(f"Invalid json in \"{path}\": {err.msg} at line {err.lineno} (col {err.colno})")
sys.exit(1)
version = int(data.get("version", "1"))
if version == 1: