diff --git a/docs/refman/loaderyaml.py b/docs/refman/loaderyaml.py index 1f8018517..9f96fa459 100644 --- a/docs/refman/loaderyaml.py +++ b/docs/refman/loaderyaml.py @@ -28,71 +28,130 @@ from .model import ( from mesonbuild import mlog from mesonbuild import mesonlib -from strictyaml import Map, MapPattern, Optional, Str, Seq, Int, Bool, load, EmptyList, OrValidator from pathlib import Path import typing as T -d_named_object = { - 'name': Str(), - 'description': Str(), -} - -d_feture_check = { - Optional('since', default=''): Str(), - Optional('deprecated', default=''): Str(), -} - -s_posarg = Map({ - **d_feture_check, - 'description': Str(), - 'type': Str(), - Optional('default', default=''): Str(), -}) - -s_varargs = Map({ - **d_named_object, **d_feture_check, - 'type': Str(), - Optional('min_varargs', default=-1): Int(), - Optional('max_varargs', default=-1): Int(), -}) - -s_kwarg = Map({ - **d_feture_check, - 'type': Str(), - 'description': Str(), - Optional('required', default=False): Bool(), - Optional('default', default=''): Str(), -}) - -s_function = Map({ - **d_named_object, **d_feture_check, - 'returns': Str(), - Optional('notes', default=[]): OrValidator(Seq(Str()), EmptyList()), - Optional('warnings', default=[]): OrValidator(Seq(Str()), EmptyList()), - Optional('example', default=''): Str(), - Optional('posargs'): MapPattern(Str(), s_posarg), - Optional('optargs'): MapPattern(Str(), s_posarg), - Optional('varargs'): s_varargs, - Optional('posargs_inherit', default=''): Str(), - Optional('optargs_inherit', default=''): Str(), - Optional('varargs_inherit', default=''): Str(), - Optional('kwargs'): MapPattern(Str(), s_kwarg), - Optional('kwargs_inherit', default=[]): OrValidator(OrValidator(Seq(Str()), EmptyList()), Str()), -}) - -s_object = Map({ - **d_named_object, **d_feture_check, - 'long_name': Str(), - Optional('extends', default=''): Str(), - Optional('notes', default=[]): OrValidator(Seq(Str()), EmptyList()), - Optional('warnings', default=[]): OrValidator(Seq(Str()), EmptyList()), - Optional('example', default=''): Str(), - Optional('methods'): Seq(s_function), - Optional('is_container', default=False): Bool() -}) +class Template: + d_feature_check: T.Dict[str, T.Any] = {} + s_posarg: T.Dict[str, T.Any] = {} + s_varargs: T.Dict[str, T.Any] = {} + s_kwarg: T.Dict[str, T.Any] = {} + s_function: T.Dict[str, T.Any] = {} + s_object: T.Dict[str, T.Any] = {} + +class StrictTemplate(Template): + def __init__(self) -> None: + from strictyaml import Map, MapPattern, Optional, Str, Seq, Int, Bool, EmptyList, OrValidator + + d_named_object = { + 'name': Str(), + 'description': Str(), + } + + d_feture_check = { + Optional('since', default=''): Str(), + Optional('deprecated', default=''): Str(), + } + + self.s_posarg = Map({ + **d_feture_check, + 'description': Str(), + 'type': Str(), + Optional('default', default=''): Str(), + }) + + self.s_varargs = Map({ + **d_named_object, **d_feture_check, + 'type': Str(), + Optional('min_varargs', default=-1): Int(), + Optional('max_varargs', default=-1): Int(), + }) + + self.s_kwarg = Map({ + **d_feture_check, + 'type': Str(), + 'description': Str(), + Optional('required', default=False): Bool(), + Optional('default', default=''): Str(), + }) + + self.s_function = Map({ + **d_named_object, **d_feture_check, + 'returns': Str(), + Optional('notes', default=[]): OrValidator(Seq(Str()), EmptyList()), + Optional('warnings', default=[]): OrValidator(Seq(Str()), EmptyList()), + Optional('example', default=''): Str(), + Optional('posargs'): MapPattern(Str(), self.s_posarg), + Optional('optargs'): MapPattern(Str(), self.s_posarg), + Optional('varargs'): self.s_varargs, + Optional('posargs_inherit', default=''): Str(), + Optional('optargs_inherit', default=''): Str(), + Optional('varargs_inherit', default=''): Str(), + Optional('kwargs'): MapPattern(Str(), self.s_kwarg), + Optional('kwargs_inherit', default=[]): OrValidator(OrValidator(Seq(Str()), EmptyList()), Str()), + }) + + self.s_object = Map({ + **d_named_object, **d_feture_check, + 'long_name': Str(), + Optional('extends', default=''): Str(), + Optional('notes', default=[]): OrValidator(Seq(Str()), EmptyList()), + Optional('warnings', default=[]): OrValidator(Seq(Str()), EmptyList()), + Optional('example', default=''): Str(), + Optional('methods'): Seq(self.s_function), + Optional('is_container', default=False): Bool() + }) + +class FastTemplate(Template): + d_feature_check: T.Dict[str, T.Any] = { + 'since': '', + 'deprecated': '', + } + + s_posarg = { + **d_feature_check, + 'default': '', + } + + s_varargs: T.Dict[str, T.Any] = { + **d_feature_check, + 'min_varargs': -1, + 'max_varargs': -1, + } + + s_kwarg = { + **d_feature_check, + 'required': False, + 'default': '', + } + + s_function = { + **d_feature_check, + 'notes': [], + 'warnings': [], + 'example': '', + 'posargs': {}, + 'optargs': {}, + 'varargs': None, + 'posargs_inherit': '', + 'optargs_inherit': '', + 'varargs_inherit': '', + 'kwargs': {}, + 'kwargs_inherit': [], + } + + s_object = { + **d_feature_check, + 'extends': '', + 'notes': [], + 'warnings': [], + 'example': '', + 'methods': [], + 'is_container': False, + } class LoaderYAML(LoaderBase): - def __init__(self, yaml_dir: Path) -> None: + def __init__(self, yaml_dir: Path, strict: bool=True) -> None: super().__init__() self.yaml_dir = yaml_dir self.func_dir = self.yaml_dir / 'functions' @@ -100,8 +159,37 @@ class LoaderYAML(LoaderBase): self.objs_dir = self.yaml_dir / 'objects' self.builtin_dir = self.yaml_dir / 'builtins' self.modules_dir = self.yaml_dir / 'modules' + self.strict = strict + + template: Template + if self.strict: + import strictyaml + def loader(file: str, template: T.Any, label: str) -> T.Dict: + r: T.Dict = strictyaml.load(file, template, label=label).data + return r - def _process_function_base(self, raw: T.OrderedDict, obj: T.Optional[Object] = None) -> Function: + self._load = loader + template = StrictTemplate() + else: + import yaml + from yaml import CLoader + def loader(file: str, template: T.Any, label: str) -> T.Dict: + return {**template, **yaml.load(file, Loader=CLoader)} + + self._load = loader + template = FastTemplate() + + self.template = template + + def _fix_default(self, v: T.Dict) -> None: + if v["default"] is False: + v["default"] = "false" + elif v["default"] is True: + v["default"] = "true" + else: + v["default"] = str(v["default"]) + + def _process_function_base(self, raw: T.Dict, obj: T.Optional[Object] = None) -> Function: # Handle arguments posargs = raw.pop('posargs', {}) optargs = raw.pop('optargs', {}) @@ -119,18 +207,29 @@ class LoaderYAML(LoaderBase): kwargs_mapped: T.Dict[str, Kwarg] = {} for k, v in posargs.items(): + if not self.strict: + v = {**self.template.s_posarg, **v} + self._fix_default(v) v['type'] = Type(v['type']) posargs_mapped += [PosArg(name=k, **v)] for k, v in optargs.items(): + if not self.strict: + v = {**self.template.s_posarg, **v} + self._fix_default(v) v['type'] = Type(v['type']) optargs_mapped += [PosArg(name=k, **v)] for k, v in kwargs.items(): + if not self.strict: + v = {**self.template.s_kwarg, **v} + self._fix_default(v) v['type'] = Type(v['type']) kwargs_mapped[k] = Kwarg(name=k, **v) if varargs is not None: + if not self.strict: + varargs = {**self.template.s_varargs, **varargs} varargs['type'] = Type(varargs['type']) varargs_mapped = VarArgs(**varargs) @@ -157,13 +256,13 @@ class LoaderYAML(LoaderBase): def _load_function(self, path: Path, obj: T.Optional[Object] = None) -> Function: path_label = path.relative_to(self.yaml_dir).as_posix() mlog.log('Loading', mlog.bold(path_label)) - raw = load(self.read_file(path), s_function, label=path_label).data + raw = self._load(self.read_file(path), self.template.s_function, label=path_label) return self._process_function_base(raw) def _load_object(self, obj_type: ObjectType, path: Path) -> Object: path_label = path.relative_to(self.yaml_dir).as_posix() mlog.log(f'Loading', mlog.bold(path_label)) - raw = load(self.read_file(path), s_object, label=path_label).data + raw = self._load(self.read_file(path), self.template.s_object, label=path_label) def as_methods(mlist: T.List[Function]) -> T.List[Method]: res: T.List[Method] = [] @@ -174,7 +273,13 @@ class LoaderYAML(LoaderBase): methods = raw.pop('methods', []) obj = Object(methods=[], obj_type=obj_type, **raw) - obj.methods = as_methods([self._process_function_base(x, obj) for x in methods]) + + methods = [] + for x in methods: + if not self.strict: + x = {**self.template.s_function, **x} + methods += [self._process_function_base(x, obj)] + obj.methods = as_methods(methods) return obj def _load_module(self, path: Path) -> T.List[Object]: diff --git a/docs/refman/main.py b/docs/refman/main.py index e2654c9f7..bf674d446 100644 --- a/docs/refman/main.py +++ b/docs/refman/main.py @@ -33,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('-l', '--loader', type=str, default='yaml', choices=['yaml', 'fastyaml', 'pickle'], help='Information loader backend') parser.add_argument('-g', '--generator', type=str, choices=['print', 'pickle', 'md', 'json', 'man'], 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') @@ -49,6 +49,7 @@ def main() -> int: loaders: T.Dict[str, T.Callable[[], LoaderBase]] = { 'yaml': lambda: LoaderYAML(args.input), + 'fastyaml': lambda: LoaderYAML(args.input, strict=False), 'pickle': lambda: LoaderPickle(args.input), }