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