Pre-compile all `excludes regular expressions in apidoc` (#10981)

Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
This commit is contained in:
Val Lorentz 2023-07-28 18:31:56 +02:00 committed by GitHub
parent f0fead5351
commit eb3fffc3e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -12,12 +12,13 @@ https://sat.qc.ca/
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import fnmatch
import glob import glob
import locale import locale
import os import os
import re
import sys import sys
from copy import copy from copy import copy
from fnmatch import fnmatch
from importlib.machinery import EXTENSION_SUFFIXES from importlib.machinery import EXTENSION_SUFFIXES
from os import path from os import path
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
@ -30,7 +31,7 @@ from sphinx.util.osutil import FileAvoidWrite, ensuredir
from sphinx.util.template import ReSTRenderer from sphinx.util.template import ReSTRenderer
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Generator from collections.abc import Generator, Sequence
# automodule options # automodule options
if 'SPHINX_APIDOC_OPTIONS' in os.environ: if 'SPHINX_APIDOC_OPTIONS' in os.environ:
@ -116,7 +117,8 @@ def create_module_file(package: str | None, basename: str, opts: Any,
def create_package_file(root: str, master_package: str | None, subroot: str, def create_package_file(root: str, master_package: str | None, subroot: str,
py_files: list[str], py_files: list[str],
opts: Any, subs: list[str], is_namespace: bool, opts: Any, subs: list[str], is_namespace: bool,
excludes: list[str] = [], user_template_dir: str | None = None, excludes: Sequence[re.Pattern[str]] = (),
user_template_dir: str | None = None,
) -> None: ) -> None:
"""Build the text of the file and write the file.""" """Build the text of the file and write the file."""
# build a list of sub packages (directories containing an __init__ file) # build a list of sub packages (directories containing an __init__ file)
@ -183,7 +185,8 @@ def create_modules_toc_file(modules: list[str], opts: Any, name: str = 'modules'
write_file(name, text, opts) write_file(name, text, opts)
def is_skipped_package(dirname: str, opts: Any, excludes: list[str] = []) -> bool: def is_skipped_package(dirname: str, opts: Any,
excludes: Sequence[re.Pattern[str]] = ()) -> bool:
"""Check if we want to skip this module.""" """Check if we want to skip this module."""
if not path.isdir(dirname): if not path.isdir(dirname):
return False return False
@ -198,7 +201,7 @@ def is_skipped_package(dirname: str, opts: Any, excludes: list[str] = []) -> boo
return all(is_excluded(path.join(dirname, f), excludes) for f in files) return all(is_excluded(path.join(dirname, f), excludes) for f in files)
def is_skipped_module(filename: str, opts: Any, excludes: list[str]) -> bool: def is_skipped_module(filename: str, opts: Any, _excludes: Sequence[re.Pattern[str]]) -> bool:
"""Check if we want to skip this module.""" """Check if we want to skip this module."""
if not path.exists(filename): if not path.exists(filename):
# skip if the file doesn't exist # skip if the file doesn't exist
@ -209,7 +212,7 @@ def is_skipped_module(filename: str, opts: Any, excludes: list[str]) -> bool:
return False return False
def walk(rootpath: str, excludes: list[str], opts: Any, def walk(rootpath: str, excludes: Sequence[re.Pattern[str]], opts: Any,
) -> Generator[tuple[str, list[str], list[str]], None, None]: ) -> Generator[tuple[str, list[str], list[str]], None, None]:
"""Walk through the directory and list files and subdirectories up.""" """Walk through the directory and list files and subdirectories up."""
followlinks = getattr(opts, 'followlinks', False) followlinks = getattr(opts, 'followlinks', False)
@ -234,7 +237,7 @@ def walk(rootpath: str, excludes: list[str], opts: Any,
yield root, subs, files yield root, subs, files
def has_child_module(rootpath: str, excludes: list[str], opts: Any) -> bool: def has_child_module(rootpath: str, excludes: Sequence[re.Pattern[str]], opts: Any) -> bool:
"""Check the given directory contains child module/s (at least one).""" """Check the given directory contains child module/s (at least one)."""
return any( return any(
files files
@ -242,7 +245,7 @@ def has_child_module(rootpath: str, excludes: list[str], opts: Any) -> bool:
) )
def recurse_tree(rootpath: str, excludes: list[str], opts: Any, def recurse_tree(rootpath: str, excludes: Sequence[re.Pattern[str]], opts: Any,
user_template_dir: str | None = None) -> list[str]: user_template_dir: str | None = None) -> list[str]:
""" """
Look for every file in the directory tree and create the corresponding Look for every file in the directory tree and create the corresponding
@ -297,16 +300,13 @@ def recurse_tree(rootpath: str, excludes: list[str], opts: Any,
return toplevels return toplevels
def is_excluded(root: str, excludes: list[str]) -> bool: def is_excluded(root: str, excludes: Sequence[re.Pattern[str]]) -> bool:
"""Check if the directory is in the exclude list. """Check if the directory is in the exclude list.
Note: by having trailing slashes, we avoid common prefix issues, like Note: by having trailing slashes, we avoid common prefix issues, like
e.g. an exclude "foo" also accidentally excluding "foobar". e.g. an exclude "foo" also accidentally excluding "foobar".
""" """
return any( return any(exclude.match(root) for exclude in excludes)
fnmatch(root, exclude)
for exclude in excludes
)
def get_parser() -> argparse.ArgumentParser: def get_parser() -> argparse.ArgumentParser:
@ -427,7 +427,10 @@ def main(argv: list[str] = sys.argv[1:]) -> int:
raise SystemExit(1) raise SystemExit(1)
if not args.dryrun: if not args.dryrun:
ensuredir(args.destdir) ensuredir(args.destdir)
excludes = [path.abspath(exclude) for exclude in args.exclude_pattern] excludes = tuple(
re.compile(fnmatch.translate(path.abspath(exclude)))
for exclude in dict.fromkeys(args.exclude_pattern)
)
modules = recurse_tree(rootpath, excludes, args, args.templatedir) modules = recurse_tree(rootpath, excludes, args, args.templatedir)
if args.full: if args.full: