meta: proper error reporting for schema parsing

When parsing the module file, parse the JSON directly from the AST
node, because the AST node contains the line number of the schema
in the  module and thus we can resolve the correct line number for
errors  within the JSON. Convert the `JSONDecodeError` to a
`SyntaxError` which results in an overall better exception message:

Before:
Traceback (most recent call last):
  File "/workspaces/osbuild/osbuild/meta.py", line 331, in get_schema
    opts = self._make_options(version)
  [...]
  File "/usr/lib64/python3.9/json/decoder.py", line 353, in raw_decode
    obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Expecting property name enclosed in
                              double quotes: line 2 column 1 (char 14)

After:
Traceback (most recent call last):
  File "/usr/lib64/python3.9/runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  [...]
    raise SyntaxError(msg, detail) from None
  File "stages/org.osbuild.ostree.init-fs", line 31
    additionalProperties: False
    ^
SyntaxError: Invalid schema: Expecting property name enclosed in ...
This commit is contained in:
Christian Kellner 2021-06-06 17:49:56 +02:00
parent 91e7708d80
commit 5707c0a5b9

View file

@ -301,8 +301,7 @@ class ModuleInfo:
raw = self.opts[fallback]
if not raw:
raise ValueError(f"Unsupported version: {version}")
return json.loads("{" + raw + "}")
return raw
def _make_options(self, version):
if version == "2":
@ -310,11 +309,11 @@ class ModuleInfo:
if not raw:
return self._make_options("1")
elif version == "1":
raw = '"options": {' + self.opts["1"] + '}'
raw = {"options": self.opts["1"]}
else:
raise ValueError(f"Unsupported version: {version}")
return json.loads("{" + raw + "}")
return raw
def get_schema(self, version="1"):
schema = {
@ -346,16 +345,29 @@ class ModuleInfo:
return schema
@classmethod
def _parse_schema(cls, klass, name, node):
if not node:
return {}
value = node.value
if not isinstance(value, ast.Str):
return {}
try:
return json.loads("{" + value.s + "}")
except json.decoder.JSONDecodeError as e:
msg = "Invalid schema: " + e.msg
line = e.doc.splitlines()[e.lineno - 1]
fullname = cls.MODULES[klass] + "/" + name
lineno = e.lineno + node.lineno - 1
detail = fullname, lineno, e.colno, line
raise SyntaxError(msg, detail) from None
@classmethod
def load(cls, root, klass, name) -> Optional["ModuleInfo"]:
names = ["SCHEMA", "SCHEMA_2"]
def value(a):
v = a.value
if isinstance(v, ast.Str):
return v.s
return ""
def filter_type(lst, target):
return [x for x in lst if isinstance(x, target)]
@ -379,13 +391,20 @@ class ModuleInfo:
doclist = docstring.split("\n")
assigns = filter_type(tree.body, ast.Assign)
targets = [(t, a) for a in assigns for t in targets(a)]
values = {k: value(v) for k, v in targets if k in names}
values = {
t: a
for a in assigns
for t in targets(a)
if t in names
}
def parse_schema(node):
return cls._parse_schema(klass, name, node)
info = {
'schema': {
"1": values.get("SCHEMA", ""),
"2": values.get("SCHEMA_2")
"1": parse_schema(values.get("SCHEMA")),
"2": parse_schema(values.get("SCHEMA_2")),
},
'desc': doclist[0],
'info': "\n".join(doclist[1:])