Skip to content

Commit 2593af2

Browse files
authored
Improve performance by skipping unnecessary normalisation (#3751)
This speeds up black by about 40% when the cache is full
1 parent f3b50e4 commit 2593af2

File tree

3 files changed

+21
-7
lines changed

3 files changed

+21
-7
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555

5656
<!-- Changes that improve Black's performance. -->
5757

58+
- Speed up _Black_ significantly when the cache is full (#3751)
5859
- Avoid importing `IPython` in a case where we wouldn't need it (#3748)
5960

6061
### Output

src/black/files.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -276,15 +276,24 @@ def normalize_path_maybe_ignore(
276276
return root_relative_path
277277

278278

279-
def path_is_ignored(
280-
path: Path, gitignore_dict: Dict[Path, PathSpec], report: Report
279+
def _path_is_ignored(
280+
root_relative_path: str,
281+
root: Path,
282+
gitignore_dict: Dict[Path, PathSpec],
283+
report: Report,
281284
) -> bool:
285+
path = root / root_relative_path
286+
# Note that this logic is sensitive to the ordering of gitignore_dict. Callers must
287+
# ensure that gitignore_dict is ordered from least specific to most specific.
282288
for gitignore_path, pattern in gitignore_dict.items():
283-
relative_path = normalize_path_maybe_ignore(path, gitignore_path, report)
284-
if relative_path is None:
289+
try:
290+
relative_path = path.relative_to(gitignore_path).as_posix()
291+
except ValueError:
285292
break
286293
if pattern.match_file(relative_path):
287-
report.path_ignored(path, "matches a .gitignore file content")
294+
report.path_ignored(
295+
path.relative_to(root), "matches a .gitignore file content"
296+
)
288297
return True
289298
return False
290299

@@ -326,7 +335,9 @@ def gen_python_files(
326335
continue
327336

328337
# First ignore files matching .gitignore, if passed
329-
if gitignore_dict and path_is_ignored(child, gitignore_dict, report):
338+
if gitignore_dict and _path_is_ignored(
339+
normalized_path, root, gitignore_dict, report
340+
):
330341
continue
331342

332343
# Then ignore with `--exclude` `--extend-exclude` and `--force-exclude` options.

tests/test_black.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,8 @@ def _mocked_calls() -> bool:
508508
"pathlib.Path.cwd", return_value=working_directory
509509
), patch("pathlib.Path.is_dir", side_effect=mock_n_calls([True])):
510510
ctx = FakeContext()
511+
# Note that the root folder (project_root) isn't the folder
512+
# named "root" (aka working_directory)
511513
ctx.obj["root"] = project_root
512514
report = MagicMock(verbose=True)
513515
black.get_sources(
@@ -527,7 +529,7 @@ def _mocked_calls() -> bool:
527529
for _, mock_args, _ in report.path_ignored.mock_calls
528530
), "A symbolic link was reported."
529531
report.path_ignored.assert_called_once_with(
530-
Path("child", "b.py"), "matches a .gitignore file content"
532+
Path("root", "child", "b.py"), "matches a .gitignore file content"
531533
)
532534

533535
def test_report_verbose(self) -> None:

0 commit comments

Comments
 (0)