|
16 | 16 | from typing import Final, Optional, Union
|
17 | 17 | from typing_extensions import TypeAlias as _TypeAlias
|
18 | 18 |
|
| 19 | +from pathspec import PathSpec |
| 20 | +from pathspec.patterns.gitwildmatch import GitWildMatchPatternError |
| 21 | + |
19 | 22 | from mypy import pyinfo
|
20 | 23 | from mypy.errors import CompileError
|
21 | 24 | from mypy.fscache import FileSystemCache
|
@@ -625,6 +628,12 @@ def find_modules_recursive(self, module: str) -> list[BuildSource]:
|
625 | 628 | subpath, self.options.exclude, self.fscache, self.options.verbosity >= 2
|
626 | 629 | ):
|
627 | 630 | continue
|
| 631 | + if ( |
| 632 | + self.options |
| 633 | + and self.options.exclude_gitignore |
| 634 | + and matches_gitignore(subpath, self.fscache, self.options.verbosity >= 2) |
| 635 | + ): |
| 636 | + continue |
628 | 637 |
|
629 | 638 | if self.fscache.isdir(subpath):
|
630 | 639 | # Only recurse into packages
|
@@ -664,6 +673,42 @@ def matches_exclude(
|
664 | 673 | return False
|
665 | 674 |
|
666 | 675 |
|
| 676 | +def matches_gitignore(subpath: str, fscache: FileSystemCache, verbose: bool) -> bool: |
| 677 | + dir, _ = os.path.split(subpath) |
| 678 | + for gi_path, gi_spec in find_gitignores(dir): |
| 679 | + relative_path = os.path.relpath(subpath, gi_path) |
| 680 | + if fscache.isdir(relative_path): |
| 681 | + relative_path = relative_path + "/" |
| 682 | + if gi_spec.match_file(relative_path): |
| 683 | + if verbose: |
| 684 | + print( |
| 685 | + f"TRACE: Excluding {relative_path} (matches .gitignore) in {gi_path}", |
| 686 | + file=sys.stderr, |
| 687 | + ) |
| 688 | + return True |
| 689 | + return False |
| 690 | + |
| 691 | + |
| 692 | +@functools.lru_cache |
| 693 | +def find_gitignores(dir: str) -> list[tuple[str, PathSpec]]: |
| 694 | + parent_dir = os.path.dirname(dir) |
| 695 | + if parent_dir == dir: |
| 696 | + parent_gitignores = [] |
| 697 | + else: |
| 698 | + parent_gitignores = find_gitignores(parent_dir) |
| 699 | + |
| 700 | + gitignore = os.path.join(dir, ".gitignore") |
| 701 | + if os.path.isfile(gitignore): |
| 702 | + with open(gitignore) as f: |
| 703 | + lines = f.readlines() |
| 704 | + try: |
| 705 | + return parent_gitignores + [(dir, PathSpec.from_lines("gitwildmatch", lines))] |
| 706 | + except GitWildMatchPatternError: |
| 707 | + print(f"error: could not parse {gitignore}", file=sys.stderr) |
| 708 | + return parent_gitignores |
| 709 | + return parent_gitignores |
| 710 | + |
| 711 | + |
667 | 712 | def is_init_file(path: str) -> bool:
|
668 | 713 | return os.path.basename(path) in ("__init__.py", "__init__.pyi")
|
669 | 714 |
|
|
0 commit comments