Esempio n. 1
0
def parse_toc_to_env(app: Sphinx, config: Config) -> None:
    """Parse the external toc file and store it in the Sphinx environment.

    Also, change the ``master_doc`` and add to ``exclude_patterns`` if necessary.
    """
    # TODO this seems to work in the tests, but I still want to double check
    external_toc_path = PurePosixPath(app.config["external_toc_path"])
    if not external_toc_path.is_absolute():
        path = Path(app.srcdir) / str(external_toc_path)
    else:
        path = Path(str(external_toc_path))
    if not path.exists():
        raise ExtensionError(f"[etoc] `external_toc_path` does not exist: {path}")
    if not path.is_file():
        raise ExtensionError(f"[etoc] `external_toc_path` is not a file: {path}")
    try:
        site_map = parse_toc_yaml(path)
    except Exception as exc:
        raise ExtensionError(f"[etoc] {exc}") from exc
    config.external_site_map = site_map

    # Update the master_doc to the root doc of the site map
    if config["master_doc"] != site_map.root.docname:
        logger.info("[etoc] Changing master_doc to '%s'", site_map.root.docname)
    config["master_doc"] = site_map.root.docname

    if config["external_toc_exclude_missing"]:
        # add files not specified in ToC file to exclude list
        new_excluded: List[str] = []
        already_excluded = Matcher(config["exclude_patterns"])
        for suffix in config["source_suffix"]:
            # recurse files in source directory, with this suffix, note
            # we do not use `Path.glob` here, since it does not ignore hidden files:
            # https://stackoverflow.com/questions/49862648/why-do-glob-glob-and-pathlib-path-glob-treat-hidden-files-differently
            for path_str in glob.iglob(
                str(Path(app.srcdir) / "**" / f"*{suffix}"), recursive=True
            ):
                path = Path(path_str)
                if not path.is_file():
                    continue
                posix = path.relative_to(app.srcdir).as_posix()
                posix_no_suffix = posix[: -len(suffix)]
                components = posix.split("/")
                if not (
                    # files can be stored with or without suffixes
                    posix in site_map
                    or posix_no_suffix in site_map
                    # ignore anything already excluded, we have to check against
                    # the file path and all its sub-directory paths
                    or any(
                        already_excluded("/".join(components[: i + 1]))
                        for i in range(len(components))
                    )
                    # don't exclude docnames matching globs
                    or any(patmatch(posix_no_suffix, pat) for pat in site_map.globs())
                ):
                    new_excluded.append(posix)
        if new_excluded:
            logger.info(
                "[etoc] Excluded %s extra file(s) not in toc", len(new_excluded)
            )
            logger.debug("[etoc] Excluded extra file(s) not in toc: %r", new_excluded)
            # Note, don't `extend` list, as it alters the default `Config.config_values`
            config["exclude_patterns"] = config["exclude_patterns"] + new_excluded