import click
import toml
from typed_ast import ast3, ast27
+from pathspec import PathSpec
# lib2to3 fork
from blib2to3.pytree import Node, Leaf, type_repr
p = Path(s)
if p.is_dir():
sources.update(
- gen_python_files_in_dir(p, root, include_regex, exclude_regex, report)
+ gen_python_files_in_dir(
+ p, root, include_regex, exclude_regex, report, get_gitignore(root)
+ )
)
elif p.is_file() or s == "-":
# if a file was explicitly given, we don't care about its extension
return imports
+@lru_cache()
+def get_gitignore(root: Path) -> PathSpec:
+ """ Return a PathSpec matching gitignore content if present."""
+ gitignore = root / ".gitignore"
+ if not gitignore.is_file():
+ return PathSpec.from_lines("gitwildmatch", [])
+ else:
+ return PathSpec.from_lines("gitwildmatch", gitignore.open())
+
+
def gen_python_files_in_dir(
path: Path,
root: Path,
include: Pattern[str],
exclude: Pattern[str],
report: "Report",
+ gitignore: PathSpec,
) -> Iterator[Path]:
"""Generate all files under `path` whose paths are not excluded by the
`exclude` regex, but are included by the `include` regex.
"""
assert root.is_absolute(), f"INTERNAL ERROR: `root` must be absolute but is {root}"
for child in path.iterdir():
+ # First ignore files matching .gitignore
+ if gitignore.match_file(child.as_posix()):
+ report.path_ignored(child, f"matches the .gitignore file content")
+ continue
+
+ # Then ignore with `exclude` option.
try:
normalized_path = "/" + child.resolve().relative_to(root).as_posix()
except ValueError:
if child.is_dir():
normalized_path += "/"
+
exclude_match = exclude.search(normalized_path)
if exclude_match and exclude_match.group(0):
report.path_ignored(child, f"matches the --exclude regular expression")
continue
if child.is_dir():
- yield from gen_python_files_in_dir(child, root, include, exclude, report)
+ yield from gen_python_files_in_dir(
+ child, root, include, exclude, report, gitignore
+ )
elif child.is_file():
include_match = include.search(normalized_path)
else:
has_blackd_deps = True
+from pathspec import PathSpec
+
ff = partial(black.format_file_in_place, mode=black.FileMode(), fast=True)
fs = partial(black.format_str, mode=black.FileMode())
THIS_FILE = Path(__file__)
include = re.compile(r"\.pyi?$")
exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
report = black.Report()
+ gitignore = PathSpec.from_lines("gitwildmatch", [])
sources: List[Path] = []
expected = [
Path(path / "b/dont_exclude/a.py"),
]
this_abs = THIS_DIR.resolve()
sources.extend(
- black.gen_python_files_in_dir(path, this_abs, include, exclude, report)
+ black.gen_python_files_in_dir(
+ path, this_abs, include, exclude, report, gitignore
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ def test_gitignore_exclude(self) -> None:
+ path = THIS_DIR / "data" / "include_exclude_tests"
+ include = re.compile(r"\.pyi?$")
+ exclude = re.compile(r"")
+ report = black.Report()
+ gitignore = PathSpec.from_lines(
+ "gitwildmatch", ["exclude/", ".definitely_exclude"]
+ )
+ sources: List[Path] = []
+ expected = [
+ Path(path / "b/dont_exclude/a.py"),
+ Path(path / "b/dont_exclude/a.pyi"),
+ ]
+ this_abs = THIS_DIR.resolve()
+ sources.extend(
+ black.gen_python_files_in_dir(
+ path, this_abs, include, exclude, report, gitignore
+ )
)
self.assertEqual(sorted(expected), sorted(sources))
def test_empty_include(self) -> None:
path = THIS_DIR / "data" / "include_exclude_tests"
report = black.Report()
+ gitignore = PathSpec.from_lines("gitwildmatch", [])
empty = re.compile(r"")
sources: List[Path] = []
expected = [
this_abs = THIS_DIR.resolve()
sources.extend(
black.gen_python_files_in_dir(
- path, this_abs, empty, re.compile(black.DEFAULT_EXCLUDES), report
+ path,
+ this_abs,
+ empty,
+ re.compile(black.DEFAULT_EXCLUDES),
+ report,
+ gitignore,
)
)
self.assertEqual(sorted(expected), sorted(sources))
def test_empty_exclude(self) -> None:
path = THIS_DIR / "data" / "include_exclude_tests"
report = black.Report()
+ gitignore = PathSpec.from_lines("gitwildmatch", [])
empty = re.compile(r"")
sources: List[Path] = []
expected = [
this_abs = THIS_DIR.resolve()
sources.extend(
black.gen_python_files_in_dir(
- path, this_abs, re.compile(black.DEFAULT_INCLUDES), empty, report
+ path,
+ this_abs,
+ re.compile(black.DEFAULT_INCLUDES),
+ empty,
+ report,
+ gitignore,
)
)
self.assertEqual(sorted(expected), sorted(sources))
include = re.compile(black.DEFAULT_INCLUDES)
exclude = re.compile(black.DEFAULT_EXCLUDES)
report = black.Report()
+ gitignore = PathSpec.from_lines("gitwildmatch", [])
# `child` should behave like a symlink which resolved path is clearly
# outside of the `root` directory.
path.iterdir.return_value = [child]
child.resolve.return_value = Path("/a/b/c")
child.is_symlink.return_value = True
try:
- list(black.gen_python_files_in_dir(path, root, include, exclude, report))
+ list(
+ black.gen_python_files_in_dir(
+ path, root, include, exclude, report, gitignore
+ )
+ )
except ValueError as ve:
self.fail(f"`get_python_files_in_dir()` failed: {ve}")
path.iterdir.assert_called_once()
# outside of the `root` directory.
child.is_symlink.return_value = False
with self.assertRaises(ValueError):
- list(black.gen_python_files_in_dir(path, root, include, exclude, report))
+ list(
+ black.gen_python_files_in_dir(
+ path, root, include, exclude, report, gitignore
+ )
+ )
path.iterdir.assert_called()
self.assertEqual(path.iterdir.call_count, 2)
child.resolve.assert_called()