diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index a272b683d..f11354900 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -18,11 +18,9 @@ from __future__ import annotations import os, subprocess import typing as T -from mesonbuild import mesonlib -from mesonbuild import mlog, build -from mesonbuild.mesonlib import MesonException -from . import ModuleReturnValue, ModuleInfo -from . import ExtensionModule +from . import ExtensionModule, ModuleReturnValue, ModuleInfo +from .. import build, mesonlib, mlog +from ..build import CustomTarget, CustomTargetIndex from ..dependencies import Dependency, InternalDependency from ..interpreterbase import ( InvalidArguments, noPosargs, noKwargs, typed_kwargs, FeatureDeprecated, @@ -30,10 +28,34 @@ from ..interpreterbase import ( ) from ..interpreter.interpreterobjects import _CustomTargetHolder from ..interpreter.type_checking import NoneType +from ..mesonlib import File, MesonException from ..programs import ExternalProgram +if T.TYPE_CHECKING: + from typing_extensions import TypedDict -def ensure_list(value): + from . import ModuleState + from ..environment import Environment + from ..interpreter import Interpreter + from ..interpreterbase import TYPE_kwargs, TYPE_var + + _T = T.TypeVar('_T') + + class GenerateDocKwargs(TypedDict): + sitemap: T.Union[str, File, CustomTarget, CustomTargetIndex] + index: T.Union[str, File, CustomTarget, CustomTargetIndex] + project_version: str + html_extra_theme: T.Optional[str] + include_paths: T.List[str] + dependencies: T.List[T.Union[Dependency, build.StaticLibrary, build.SharedLibrary, CustomTarget, CustomTargetIndex]] + depends: T.List[T.Union[CustomTarget, CustomTargetIndex]] + gi_c_source_roots: T.List[str] + extra_assets: T.List[str] + extra_extension_paths: T.List[str] + subprojects: T.List['HotdocTarget'] + install: bool + +def ensure_list(value: T.Union[_T, T.List[_T]]) -> T.List[_T]: if not isinstance(value, list): return [value] return value @@ -41,7 +63,7 @@ def ensure_list(value): MIN_HOTDOC_VERSION = '0.8.100' -file_types = (str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex) +file_types = (str, File, CustomTarget, CustomTargetIndex) class HotdocExternalProgram(ExternalProgram): @@ -51,29 +73,29 @@ class HotdocExternalProgram(ExternalProgram): class HotdocTargetBuilder: - def __init__(self, name, state, hotdoc, interpreter, kwargs): + def __init__(self, name: str, state: ModuleState, hotdoc: HotdocExternalProgram, interpreter: Interpreter, kwargs): self.hotdoc = hotdoc self.build_by_default = kwargs.pop('build_by_default', False) self.kwargs = kwargs self.name = name self.state = state self.interpreter = interpreter - self.include_paths = mesonlib.OrderedSet() + self.include_paths: mesonlib.OrderedSet[str] = mesonlib.OrderedSet() self.builddir = state.environment.get_build_dir() self.sourcedir = state.environment.get_source_dir() self.subdir = state.subdir self.build_command = state.environment.get_build_command() - self.cmd = ['conf', '--project-name', name, "--disable-incremental-build", - '--output', os.path.join(self.builddir, self.subdir, self.name + '-doc')] + self.cmd: T.List[TYPE_var] = ['conf', '--project-name', name, "--disable-incremental-build", + '--output', os.path.join(self.builddir, self.subdir, self.name + '-doc')] self._extra_extension_paths = set() self.extra_assets = set() self.extra_depends = [] self._subprojects = [] - def process_known_arg(self, option, argname=None, value_processor=None): + def process_known_arg(self, option: str, argname: T.Optional[str] = None, value_processor: T.Optional[T.Callable] = None) -> None: if not argname: argname = option.strip("-").replace("-", "_") @@ -83,7 +105,7 @@ class HotdocTargetBuilder: self.set_arg_value(option, value) - def set_arg_value(self, option, value): + def set_arg_value(self, option: str, value: TYPE_var) -> None: if value is None: return @@ -116,18 +138,18 @@ class HotdocTargetBuilder: else: self.cmd.extend([option, value]) - def check_extra_arg_type(self, arg, value): + def check_extra_arg_type(self, arg: str, value: TYPE_var) -> None: if isinstance(value, list): for v in value: self.check_extra_arg_type(arg, v) return - valid_types = (str, bool, mesonlib.File, build.IncludeDirs, build.CustomTarget, build.CustomTargetIndex, build.BuildTarget) + valid_types = (str, bool, File, build.IncludeDirs, CustomTarget, CustomTargetIndex, build.BuildTarget) if not isinstance(value, valid_types): raise InvalidArguments('Argument "{}={}" should be of type: {}.'.format( arg, value, [t.__name__ for t in valid_types])) - def process_extra_args(self): + def process_extra_args(self) -> None: for arg, value in self.kwargs.items(): option = "--" + arg.replace("_", "-") self.check_extra_arg_type(arg, value) @@ -158,7 +180,7 @@ class HotdocTargetBuilder: return None, None - def add_extension_paths(self, paths): + def add_extension_paths(self, paths: T.Union[T.List[str], T.Set[str]]) -> None: for path in paths: if path in self._extra_extension_paths: continue @@ -166,10 +188,10 @@ class HotdocTargetBuilder: self._extra_extension_paths.add(path) self.cmd.extend(["--extra-extension-path", path]) - def replace_dirs_in_string(self, string): + def replace_dirs_in_string(self, string: str) -> str: return string.replace("@SOURCE_ROOT@", self.sourcedir).replace("@BUILD_ROOT@", self.builddir) - def process_gi_c_source_roots(self): + def process_gi_c_source_roots(self) -> None: if self.hotdoc.run_hotdoc(['--has-extension=gi-extension']) != 0: return @@ -181,7 +203,7 @@ class HotdocTargetBuilder: self.cmd += ['--gi-c-source-roots'] + value - def process_dependencies(self, deps): + def process_dependencies(self, deps: T.List[T.Union[Dependency, build.StaticLibrary, build.SharedLibrary, CustomTarget, CustomTargetIndex]]) -> T.List[str]: cflags = set() for dep in mesonlib.listify(ensure_list(deps)): if isinstance(dep, InternalDependency): @@ -205,29 +227,29 @@ class HotdocTargetBuilder: self.include_paths.add(os.path.join(self.builddir, dep.hotdoc_conf.subdir)) self.cmd += ['--extra-assets=' + p for p in dep.extra_assets] self.add_extension_paths(dep.extra_extension_paths) - elif isinstance(dep, (build.CustomTarget, build.BuildTarget)): + elif isinstance(dep, (CustomTarget, build.BuildTarget)): self.extra_depends.append(dep) - elif isinstance(dep, build.CustomTargetIndex): + elif isinstance(dep, CustomTargetIndex): self.extra_depends.append(dep.target) return [f.strip('-I') for f in cflags] - def process_extra_assets(self): + def process_extra_assets(self) -> None: self._extra_assets = self.kwargs.pop('extra_assets') for assets_path in self._extra_assets: self.cmd.extend(["--extra-assets", assets_path]) - def process_subprojects(self): + def process_subprojects(self) -> None: value = self.kwargs.pop('subprojects') self.process_dependencies(value) self._subprojects.extend(value) - def flatten_config_command(self): + def flatten_config_command(self) -> T.List[str]: cmd = [] for arg in mesonlib.listify(self.cmd, flatten=True): - if isinstance(arg, mesonlib.File): + if isinstance(arg, File): arg = arg.absolute_path(self.state.environment.get_source_dir(), self.state.environment.get_build_dir()) elif isinstance(arg, build.IncludeDirs): @@ -236,10 +258,10 @@ class HotdocTargetBuilder: cmd.append(os.path.join(self.builddir, arg.get_curdir(), inc_dir)) continue - elif isinstance(arg, (build.BuildTarget, build.CustomTarget)): + elif isinstance(arg, (build.BuildTarget, CustomTarget)): self.extra_depends.append(arg) arg = self.interpreter.backend.get_target_filename_abs(arg) - elif isinstance(arg, build.CustomTargetIndex): + elif isinstance(arg, CustomTargetIndex): self.extra_depends.append(arg.target) arg = self.interpreter.backend.get_target_filename_abs(arg) @@ -247,7 +269,7 @@ class HotdocTargetBuilder: return cmd - def generate_hotdoc_config(self): + def generate_hotdoc_config(self) -> None: cwd = os.path.abspath(os.curdir) ncwd = os.path.join(self.sourcedir, self.subdir) mlog.log('Generating Hotdoc configuration for: ', mlog.bold(self.name)) @@ -256,7 +278,7 @@ class HotdocTargetBuilder: raise MesonException('hotdoc failed to configure') os.chdir(cwd) - def ensure_file(self, value): + def ensure_file(self, value: T.Union[str, File, CustomTarget, CustomTargetIndex]) -> T.Union[File, CustomTarget, CustomTargetIndex]: if isinstance(value, list): res = [] for val in value: @@ -264,11 +286,11 @@ class HotdocTargetBuilder: return res if isinstance(value, str): - return mesonlib.File.from_source_file(self.sourcedir, self.subdir, value) + return File.from_source_file(self.sourcedir, self.subdir, value) return value - def ensure_dir(self, value): + def ensure_dir(self, value: str) -> str: if os.path.isabs(value): _dir = value else: @@ -279,12 +301,12 @@ class HotdocTargetBuilder: return os.path.relpath(_dir, os.path.join(self.builddir, self.subdir)) - def check_forbidden_args(self): + def check_forbidden_args(self) -> None: for arg in ['conf_file']: if arg in self.kwargs: raise InvalidArguments(f'Argument "{arg}" is forbidden.') - def make_targets(self): + def make_targets(self) -> T.Tuple[HotdocTarget, mesonlib.ExecutableSerialisation]: self.check_forbidden_args() self.process_known_arg("--index", value_processor=self.ensure_file) self.process_known_arg("--project-version") @@ -330,7 +352,7 @@ class HotdocTargetBuilder: subdir=self.subdir, subproject=self.state.subproject, environment=self.state.environment, - hotdoc_conf=mesonlib.File.from_built_file( + hotdoc_conf=File.from_built_file( self.subdir, hotdoc_config_name), extra_extension_paths=self._extra_extension_paths, extra_assets=self._extra_assets, @@ -370,28 +392,29 @@ class HotdocTargetBuilder: class HotdocTargetHolder(_CustomTargetHolder['HotdocTarget']): - def __init__(self, target: HotdocTarget, interp): + def __init__(self, target: HotdocTarget, interp: Interpreter): super().__init__(target, interp) self.methods.update({'config_path': self.config_path_method}) @noPosargs @noKwargs - def config_path_method(self, *args, **kwargs): + def config_path_method(self, *args: T.Any, **kwargs: T.Any) -> str: conf = self.held_object.hotdoc_conf.absolute_path(self.interpreter.environment.source_dir, self.interpreter.environment.build_dir) return conf -class HotdocTarget(build.CustomTarget): - def __init__(self, name, subdir, subproject, hotdoc_conf, extra_extension_paths, extra_assets, - subprojects, environment, **kwargs): +class HotdocTarget(CustomTarget): + def __init__(self, name: str, subdir: str, subproject: str, hotdoc_conf: File, + extra_extension_paths: T.Set[str], extra_assets: T.List[str], + subprojects: T.List['HotdocTarget'], environment: Environment, **kwargs: T.Any): super().__init__(name, subdir, subproject, environment, **kwargs, absolute_paths=True) self.hotdoc_conf = hotdoc_conf self.extra_extension_paths = extra_extension_paths self.extra_assets = extra_assets self.subprojects = subprojects - def __getstate__(self): + def __getstate__(self) -> dict: # Make sure we do not try to pickle subprojects res = self.__dict__.copy() res['subprojects'] = [] @@ -403,7 +426,7 @@ class HotDocModule(ExtensionModule): INFO = ModuleInfo('hotdoc', '0.48.0') - def __init__(self, interpreter): + def __init__(self, interpreter: Interpreter): super().__init__(interpreter) self.hotdoc = HotdocExternalProgram('hotdoc') if not self.hotdoc.found(): @@ -419,7 +442,7 @@ class HotDocModule(ExtensionModule): @noKwargs @typed_pos_args('hotdoc.has_extensions', varargs=str, min_varargs=1) - def has_extensions(self, state, args, kwargs): + def has_extensions(self, state: ModuleState, args: T.Tuple[T.List[str]], kwargs: TYPE_kwargs) -> bool: return self.hotdoc.run_hotdoc([f'--has-extension={extension}' for extension in args[0]]) == 0 @typed_pos_args('hotdoc.generate_doc', str) @@ -434,13 +457,13 @@ class HotDocModule(ExtensionModule): KwargInfo( 'dependencies', ContainerTypeInfo(list, (Dependency, build.StaticLibrary, build.SharedLibrary, - build.CustomTarget, build.CustomTargetIndex)), + CustomTarget, CustomTargetIndex)), listify=True, default=[], ), KwargInfo( 'depends', - ContainerTypeInfo(list, (build.CustomTarget, build.CustomTargetIndex)), + ContainerTypeInfo(list, (CustomTarget, CustomTargetIndex)), listify=True, default=[], since='0.64.1', @@ -452,21 +475,21 @@ class HotDocModule(ExtensionModule): KwargInfo('install', bool, default=False), allow_unknown=True ) - def generate_doc(self, state, args, kwargs): + def generate_doc(self, state: ModuleState, args: T.Tuple[str], kwargs: GenerateDocKwargs) -> ModuleReturnValue: project_name = args[0] - if any(isinstance(x, (build.CustomTarget, build.CustomTargetIndex)) for x in kwargs['dependencies']): + if any(isinstance(x, (CustomTarget, CustomTargetIndex)) for x in kwargs['dependencies']): FeatureDeprecated.single_use('hotdoc.generate_doc dependencies argument with custom_target', '0.64.1', state.subproject, 'use `depends`', state.current_node) builder = HotdocTargetBuilder(project_name, state, self.hotdoc, self.interpreter, kwargs) target, install_script = builder.make_targets() - targets = [target] + targets: T.List[T.Union[HotdocTarget, mesonlib.ExecutableSerialisation]] = [target] if install_script: targets.append(install_script) - return ModuleReturnValue(targets[0], targets) + return ModuleReturnValue(target, targets) -def initialize(interpreter): +def initialize(interpreter: Interpreter) -> HotDocModule: mod = HotDocModule(interpreter) mod.interpreter.append_holder_map(HotdocTarget, HotdocTargetHolder) return mod