def normalize_path_maybe_ignore(path: Path, root: Path, report: Report) -> Optional[str]: """Normalize `path`. May return `None` if `path` was ignored. `report` is where "path ignored" output goes. """ try: abspath = path if path.is_absolute() else Path.cwd() / path normalized_path = abspath.resolve().relative_to(root).as_posix() except OSError as e: report.path_ignored(path, f"cannot be read because {e}") return None except ValueError: if path.is_symlink(): report.path_ignored( path, f"is a symbolic link that points outside {root}") return None raise return normalized_path
def gen_python_files( paths: Iterable[Path], root: Path, include: Pattern[str], exclude: Pattern[str], extend_exclude: Optional[Pattern[str]], force_exclude: Optional[Pattern[str]], report: Report, gitignore: Optional[PathSpec], *, verbose: bool, quiet: bool, ) -> Iterator[Path]: """Generate all files under `path` whose paths are not excluded by the `exclude_regex`, `extend_exclude`, or `force_exclude` regexes, but are included by the `include` regex. Symbolic links pointing outside of the `root` directory are ignored. `report` is where output about exclusions goes. """ assert root.is_absolute( ), f"INTERNAL ERROR: `root` must be absolute but is {root}" for child in paths: normalized_path = normalize_path_maybe_ignore(child, root, report) if normalized_path is None: continue # First ignore files matching .gitignore, if passed if gitignore is not None and gitignore.match_file(normalized_path): report.path_ignored(child, "matches the .gitignore file content") continue # Then ignore with `--exclude` `--extend-exclude` and `--force-exclude` options. normalized_path = "/" + normalized_path if child.is_dir(): normalized_path += "/" if path_is_excluded(normalized_path, exclude): report.path_ignored(child, "matches the --exclude regular expression") continue if path_is_excluded(normalized_path, extend_exclude): report.path_ignored( child, "matches the --extend-exclude regular expression") continue if path_is_excluded(normalized_path, force_exclude): report.path_ignored( child, "matches the --force-exclude regular expression") continue if child.is_dir(): # If gitignore is None, gitignore usage is disabled, while a Falsey # gitignore is when the directory doesn't have a .gitignore file. yield from gen_python_files( child.iterdir(), root, include, exclude, extend_exclude, force_exclude, report, gitignore + get_gitignore(child) if gitignore is not None else None, verbose=verbose, quiet=quiet, ) elif child.is_file(): if child.suffix == ".ipynb" and not jupyter_dependencies_are_installed( verbose=verbose, quiet=quiet): continue include_match = include.search( normalized_path) if include else True if include_match: yield child