meta: support format version 2 for module infos

When loading the schema information via the source code of a
module, look for a `SCHEMA_2` global variable, representing
the schema version 2. Extend the `get_schema` method so in
takes a `version` keyword argument. Rework the code so that
if version 2 for the format is specified but no dedicated
schema data is found, a fallback based on the version 1 is
provided. This makes it easy to use all existing stages
without explicitly duplicating all schema information.

NB: The code is not very pretty, the hope is that in the
future, the module, being an executable, could be called
with a command line switch, a la `--schema <version>` and
this would return the schema data. So that hackery code
we currently have will hopefully vanish soon. I am sorry
though for this mess.
This commit is contained in:
Christian Kellner 2021-02-10 17:39:15 +00:00
parent 23df7a4c02
commit 63eb7303e9

View file

@ -287,42 +287,60 @@ class ModuleInfo:
self.desc = info["desc"] self.desc = info["desc"]
self.opts = info["schema"] self.opts = info["schema"]
def get_schema(self): def _load_opts(self, version, fallback=None):
raw = self.opts[version]
if not raw and fallback:
raw = self.opts[fallback]
if not raw:
raise ValueError(f"Unsupported version: {version}")
return json.loads("{" + raw + "}")
def _make_options(self, version):
if version == "2":
raw = self.opts["2"]
if not raw:
return self._make_options("1")
elif version == "1":
raw = '"options": {' + self.opts["1"] + '}'
else:
raise ValueError(f"Unsupported version: {version}")
return json.loads("{" + raw + "}")
def get_schema(self, version="1"):
schema = { schema = {
"title": f"Pipeline {self.type}", "title": f"Pipeline {self.type}",
"type": "object", "type": "object",
"additionalProperties": False, "additionalProperties": False,
} }
raw = self.opts["1"]
opts = json.loads("{" + raw + "}")
if self.type in ("Stage", "Assembler"): if self.type in ("Stage", "Assembler"):
type_id = "type" if version == "2" else "name"
opts = self._make_options(version)
schema["properties"] = { schema["properties"] = {
"name": {"enum": [self.name]}, type_id: {"enum": [self.name]},
"options": { **opts,
"type": "object",
**opts
}
} }
schema["required"] = ["name"] schema["required"] = [type_id]
else: else:
opts = self._load_opts(version, "1")
schema.update(opts) schema.update(opts)
# if there are is a definitions node, it needs to be at # if there are is a definitions node, it needs to be at
# the top level schema node, since the schema inside the # the top level schema node, since the schema inside the
# stages is written as-if they were the root node and # stages is written as-if they were the root node and
# so are the references # so are the references
definitions = opts.get("definitions") options = schema.get("properties", {}).get("options", {})
if definitions: if "definitions" in options:
schema["definitions"] = definitions schema["definitions"] = options["definitions"]
del schema["properties"]["options"]["definitions"] del options["definitions"]
return schema return schema
@classmethod @classmethod
def load(cls, root, klass, name) -> Optional["ModuleInfo"]: def load(cls, root, klass, name) -> Optional["ModuleInfo"]:
names = ['SCHEMA'] names = ["SCHEMA", "SCHEMA_2"]
def value(a): def value(a):
v = a.value v = a.value
@ -359,7 +377,8 @@ class ModuleInfo:
info = { info = {
'schema': { 'schema': {
"1": values.get("SCHEMA", "") "1": values.get("SCHEMA", ""),
"2": values.get("SCHEMA_2")
}, },
'desc': doclist[0], 'desc': doclist[0],
'info': "\n".join(doclist[1:]) 'info': "\n".join(doclist[1:])