def add_sidebars(self, pagename, ctx): def has_wildcard(pattern): return any(char in pattern for char in '*?[') sidebars = None matched = None customsidebar = None for pattern, patsidebars in self.config.html_sidebars.iteritems(): if patmatch(pagename, pattern): if matched: if has_wildcard(pattern): # warn if both patterns contain wildcards if has_wildcard(matched): self.warn('page %s matches two patterns in ' 'html_sidebars: %r and %r' % (pagename, matched, pattern)) # else the already matched pattern is more specific # than the present one, because it contains no wildcard continue matched = pattern sidebars = patsidebars if sidebars is None: # keep defaults pass elif isinstance(sidebars, basestring): # 0.x compatible mode: insert custom sidebar before searchbox customsidebar = sidebars sidebars = None ctx['sidebars'] = sidebars ctx['customsidebar'] = customsidebar
def add_sidebars(self, pagename, ctx): def has_wildcard(pattern): return any(char in pattern for char in '*?[') sidebars = None matched = None customsidebar = None for pattern, patsidebars in iteritems(self.config.html_sidebars): if patmatch(pagename, pattern): if matched: if has_wildcard(pattern): # warn if both patterns contain wildcards if has_wildcard(matched): self.warn('page %s matches two patterns in ' 'html_sidebars: %r and %r' % (pagename, matched, pattern)) # else the already matched pattern is more specific # than the present one, because it contains no wildcard continue matched = pattern sidebars = patsidebars if sidebars is None: # keep defaults pass elif isinstance(sidebars, string_types): # 0.x compatible mode: insert custom sidebar before searchbox customsidebar = sidebars sidebars = None ctx['sidebars'] = sidebars ctx['customsidebar'] = customsidebar
def add_sidebars(self, pagename, ctx): # type: (str, Dict) -> None def has_wildcard(pattern): # type: (str) -> bool return any(char in pattern for char in '*?[') sidebars = None matched = None customsidebar = None # default sidebars settings for selected theme if self.theme.name == 'alabaster': # provide default settings for alabaster (for compatibility) # Note: this will be removed before Sphinx-2.0 try: # get default sidebars settings from alabaster (if defined) theme_default_sidebars = self.theme.config.get('theme', 'sidebars') if theme_default_sidebars: sidebars = [name.strip() for name in theme_default_sidebars.split(',')] except Exception: # fallback to better default settings sidebars = ['about.html', 'navigation.html', 'relations.html', 'searchbox.html', 'donate.html'] else: theme_default_sidebars = self.theme.get_config('theme', 'sidebars', None) if theme_default_sidebars: sidebars = [name.strip() for name in theme_default_sidebars.split(',')] # user sidebar settings html_sidebars = self.get_builder_config('sidebars', 'html') for pattern, patsidebars in html_sidebars.items(): if patmatch(pagename, pattern): if matched: if has_wildcard(pattern): # warn if both patterns contain wildcards if has_wildcard(matched): logger.warning(__('page %s matches two patterns in ' 'html_sidebars: %r and %r'), pagename, matched, pattern) # else the already matched pattern is more specific # than the present one, because it contains no wildcard continue matched = pattern sidebars = patsidebars if sidebars is None: # keep defaults pass ctx['sidebars'] = sidebars ctx['customsidebar'] = customsidebar
def bestmatch(patmap, source, default=None, param="source"): """return best match given a dictionary mapping glob pattersn -> values""" best = None best_rank = None for pattern in patmap: if not patmatch(pattern, source): continue cur_rank = _rank_pattern(pattern) if best is None or cur_rank < best_rank: best = pattern best_rank = cur_rank elif cur_rank == best_rank: raise KeyError("%s %r matches too many patterns: %r and %r" % (param, source, best, pattern)) if best is None: return default else: return patmap[best]
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