DocuGen AI respects .gitignore rules to exclude files from documentation generation. The scanner (docugen/core/scanner.py) implements a robust gitignore parser that follows Git’s matching behavior.
Each rule is parsed into a GitIgnoreRule dataclass (docugen/core/scanner.py:20):
@dataclass(frozen=True)class GitIgnoreRule: pattern: str # The glob pattern to match negated: bool # True if pattern starts with ! directory_only: bool # True if pattern ends with / anchored: bool # True if pattern starts with /
The _load_gitignore_rules() function (docugen/core/scanner.py:28) parses .gitignore:
def _load_gitignore_rules(root: Path) -> list[GitIgnoreRule]: gitignore_path = root / ".gitignore" if not gitignore_path.exists(): return [] rules: list[GitIgnoreRule] = [] for raw_line in gitignore_path.read_text(encoding="utf-8", errors="ignore").splitlines(): line = raw_line.strip() if not line or line.startswith("#"): continue negated = line.startswith("!") if negated: line = line[1:] directory_only = line.endswith("/") if directory_only: line = line[:-1] anchored = line.startswith("/") if anchored: line = line[1:] if line: rules.append( GitIgnoreRule( pattern=line, negated=negated, directory_only=directory_only, anchored=anchored, ) ) return rules
# Ignore all Python bytecode*.pyc__pycache__/# Ignore environment files.env.env.local# Ignore build outputs/dist//build/*.egg-info/# Ignore IDE files.vscode/.idea/*.swp# Ignore test outputs.pytest_cache/.coveragehtmlcov/# But keep example configs!config/example.env
You cannot override DEFAULT_IGNORED_DIRS through .gitignore. To include a default-ignored directory, you must modify the source:
from docugen.core.scanner import scan_python_files, DEFAULT_IGNORED_DIRS# Remove a default exclusionDEFAULT_IGNORED_DIRS.discard(".venv")# Scan with modified defaultsfiles = scan_python_files("/path/to/project")
Modifying DEFAULT_IGNORED_DIRS affects the global state. Do this early in your application.
The scan_python_files() function (docugen/core/scanner.py:94) orchestrates scanning:
def scan_python_files(root_path: str | Path) -> list[Path]: root = Path(root_path).expanduser().resolve() if not root.exists(): raise FileNotFoundError(f"Path does not exist: {root}") if root.is_file(): return [root] if root.suffix == ".py" else [] rules = _load_gitignore_rules(root) discovered: list[Path] = [] for current_dir, dirnames, filenames in os.walk(root, topdown=True): current = Path(current_dir) # Filter directories filtered_dirs: list[str] = [] for dirname in dirnames: if dirname in DEFAULT_IGNORED_DIRS: continue directory_path = current / dirname relative_dir = directory_path.relative_to(root).as_posix() if _is_ignored(relative_dir, is_dir=True, rules=rules): continue filtered_dirs.append(dirname) dirnames[:] = filtered_dirs # Modify in-place to prune walk # Filter files for filename in filenames: if not filename.endswith(".py"): continue file_path = current / filename relative_file = file_path.relative_to(root).as_posix() if _is_ignored(relative_file, is_dir=False, rules=rules): continue discovered.append(file_path.resolve()) return sorted(discovered)