docs: Added JSON generator

pull/9377/head
Daniel Mensinger 3 years ago
parent ba93dd20ca
commit d427c8fdb6
No known key found for this signature in database
GPG Key ID: 54DD94C131E277D4
  1. 120
      docs/refman/generatorjson.py
  2. 87
      docs/refman/jsonschema.py
  3. 4
      docs/refman/main.py

@ -0,0 +1,120 @@
# SPDX-License-Identifer: Apache-2.0
# Copyright 2021 The Meson development team
from pathlib import Path
import json
import re
from .generatorbase import GeneratorBase
from . import jsonschema as J
from .model import (
ReferenceManual,
Function,
Object,
Type,
PosArg,
VarArgs,
Kwarg,
)
import typing as T
class GeneratorJSON(GeneratorBase):
def __init__(self, manual: ReferenceManual, out: Path, enable_modules: bool) -> None:
super().__init__(manual)
self.out = out
self.enable_modules = enable_modules
def _generate_type(self, typ: Type) -> T.List[J.Type]:
return [
{
'obj': x.data_type.name,
'holds': self._generate_type(x.holds) if x.holds else [],
}
for x in typ.resolved
]
def _generate_type_str(self, typ: Type) -> str:
# Remove all whitespaces
return re.sub(r'[ \n\r\t]', '', typ.raw)
def _generate_arg(self, arg: T.Union[PosArg, VarArgs, Kwarg], isOptarg: bool = False) -> J.Argument:
return {
'name': arg.name,
'description': arg.description,
'since': arg.since if arg.since else None,
'deprecated': arg.deprecated if arg.deprecated else None,
'type': self._generate_type(arg.type),
'type_str': self._generate_type_str(arg.type),
'required': arg.required if isinstance(arg, Kwarg) else not isOptarg and not isinstance(arg, VarArgs),
'default': arg.default if isinstance(arg, (PosArg, Kwarg)) else None,
'min_varargs': arg.min_varargs if isinstance(arg, VarArgs) and arg.min_varargs > 0 else None,
'max_varargs': arg.max_varargs if isinstance(arg, VarArgs) and arg.max_varargs > 0 else None,
# Not yet supported
'notes': [],
'warnings': [],
}
def _generate_function(self, func: Function) -> J.Function:
return {
'name': func.name,
'description': func.description,
'since': func.since if func.since else None,
'deprecated': func.deprecated if func.deprecated else None,
'notes': func.notes,
'warnings': func.warnings,
'example': func.example if func.example else None,
'returns': self._generate_type(func.returns),
'returns_str': self._generate_type_str(func.returns),
'posargs': {x.name: self._generate_arg(x) for x in func.posargs},
'optargs': {x.name: self._generate_arg(x, True) for x in func.optargs},
'kwargs': {x.name: self._generate_arg(x) for x in self.sorted_and_filtered(list(func.kwargs.values()))},
'varargs': self._generate_arg(func.varargs) if func.varargs else None,
}
def _generate_objects(self, obj: Object) -> J.Object:
return {
'name': obj.name,
'description': obj.description,
'since': obj.since if obj.since else None,
'deprecated': obj.deprecated if obj.deprecated else None,
'notes': obj.notes,
'warnings': obj.warnings,
'defined_by_module': obj.defined_by_module.name if obj.defined_by_module else None,
'object_type': obj.obj_type.name,
'is_container': obj.is_container,
'example': obj.example if obj.example else None,
'extends': obj.extends if obj.extends else None,
'returned_by': [x.name for x in self.sorted_and_filtered(obj.returned_by)],
'extended_by': [x.name for x in self.sorted_and_filtered(obj.extended_by)],
'methods': {x.name: self._generate_function(x) for x in self.sorted_and_filtered(obj.methods)},
}
def _extract_meson_version(self) -> str:
# Hack around python relative imports to get to the Meson version
import sys
sys.path.append(Path(__file__).resolve().parents[2].as_posix())
from mesonbuild.coredata import version
return version
def generate(self) -> None:
data: J.Root = {
'version_major': J.VERSION_MAJOR,
'version_minor': J.VERSION_MINOR,
'meson_version': self._extract_meson_version(),
'functions': {x.name: self._generate_function(x) for x in self.sorted_and_filtered(self.functions)},
'objects': {x.name: self._generate_objects(x) for x in self.sorted_and_filtered(self.objects)},
'objects_by_type': {
'elementary': [x.name for x in self.elementary],
'builtins': [x.name for x in self.builtins],
'returned': [x.name for x in self.returned],
'modules': {
x.name: [y.name for y in self.sorted_and_filtered(self.extract_returned_by_module(x))]
for x in self.modules
},
},
}
self.out.write_text(json.dumps(data), encoding='utf-8')

@ -0,0 +1,87 @@
# SPDX-License-Identifer: Apache-2.0
# Copyright 2021 The Meson development team
import typing as T
# The following variables define the current version of
# the JSON documentation format. This is different from
# the Meson version
VERSION_MAJOR = 1 # Changes here indicate breaking format changes (changes to existing keys)
VERSION_MINOR = 0 # Changes here indicate non-breaking changes (only new keys are added to the existing structure)
class BaseObject(T.TypedDict):
'''
Base object for most dicts in the JSON doc.
All objects inheriting from BaseObject will support
the keys specified here:
'''
name: str
description: str
since: T.Optional[str]
deprecated: T.Optional[str]
notes: T.List[str]
warnings: T.List[str]
class Type(T.TypedDict):
obj: str # References an object from `root.objects`
holds: T.Sequence[object] # Mypy does not support recusive dicts, but this should be T.List[Type]...
class Argument(BaseObject):
'''
Object that represents any type of a single function or method argumet.
'''
type: T.List[Type] # A non-empty list of types that are supported.
type_str: str # Formated version of `type`. Is guranteed to not contain any whitespaces.
required: bool
default: T.Optional[str]
min_varargs: T.Optional[int] # Only relevant for varargs, must be `null` for all other types of arguments
max_varargs: T.Optional[int] # Only relevant for varargs, must be `null` for all other types of arguments
class Function(BaseObject):
'''
Represents a function or method.
'''
returns: T.List[Type] # A non-empty list of types that are supported.
returns_str: str # Formated version of `returns`. Is guranteed to not contain any whitespaces.
example: T.Optional[str]
posargs: T.Dict[str, Argument]
optargs: T.Dict[str, Argument]
kwargs: T.Dict[str, Argument]
varargs: T.Optional[Argument]
class Object(BaseObject):
'''
Represents all types of Meson objects. The specific object type is stored in the `object_type` field.
'''
example: T.Optional[str]
object_type: str # Defines the object type: Must be one of: ELEMENTARY, BUILTIN, MODULE, RETURNED
methods: T.Dict[str, Function]
is_container: bool
extends: T.Optional[str]
returned_by: T.List[str]
extended_by: T.List[str]
defined_by_module: T.Optional[str]
class ObjectsByType(T.TypedDict):
'''
References to other objects are stored here for ease of navigation / filtering
'''
elementary: T.List[str]
builtins: T.List[str]
returned: T.List[str]
modules: T.Dict[str, T.List[str]]
class Root(T.TypedDict):
'''
The root object of the JSON reference manual
'''
version_major: int # See the description above for
version_minor: int # VERSION_MAJOR and VERSION_MINOR
meson_version: str
functions: T.Dict[str, Function] # A mapping of <name> to a `Function` object for *all* Meson functions
objects: T.Dict[str, Object] # A mapping of <name> to a `Object` object for *all* Meson objects (including modules, elementary, etc.)
objects_by_type: ObjectsByType

@ -23,6 +23,7 @@ from .loaderpickle import LoaderPickle
from .loaderyaml import LoaderYAML
from .generatorbase import GeneratorBase
from .generatorjson import GeneratorJSON
from .generatorprint import GeneratorPrint
from .generatorpickle import GeneratorPickle
from .generatormd import GeneratorMD
@ -32,7 +33,7 @@ meson_root = Path(__file__).absolute().parents[2]
def main() -> int:
parser = argparse.ArgumentParser(description='Meson reference manual generator')
parser.add_argument('-l', '--loader', type=str, default='yaml', choices=['yaml', 'pickle'], help='Information loader backend')
parser.add_argument('-g', '--generator', type=str, choices=['print', 'pickle', 'md'], required=True, help='Generator backend')
parser.add_argument('-g', '--generator', type=str, choices=['print', 'pickle', 'md', 'json'], required=True, help='Generator backend')
parser.add_argument('-s', '--sitemap', type=Path, default=meson_root / 'docs' / 'sitemap.txt', help='Path to the input sitemap.txt')
parser.add_argument('-o', '--out', type=Path, required=True, help='Output directory for generated files')
parser.add_argument('-i', '--input', type=Path, default=meson_root / 'docs' / 'yaml', help='Input path for the selected loader')
@ -57,6 +58,7 @@ def main() -> int:
'print': lambda: GeneratorPrint(refMan),
'pickle': lambda: GeneratorPickle(refMan, args.out),
'md': lambda: GeneratorMD(refMan, args.out, args.sitemap, args.link_defs, not args.no_modules),
'json': lambda: GeneratorJSON(refMan, args.out, not args.no_modules),
}
generator = generators[args.generator]()

Loading…
Cancel
Save