Improve performance by skipping unnecessary normalisation (#3751)

This speeds up black by about 40% when the cache is full
This commit is contained in:
Shantanu 2023-07-09 15:24:01 -07:00 committed by GitHub
parent f3b50e4669
commit 2593af2c5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 21 additions and 7 deletions

View File

@ -55,6 +55,7 @@
<!-- Changes that improve Black's performance. --> <!-- Changes that improve Black's performance. -->
- Speed up _Black_ significantly when the cache is full (#3751)
- Avoid importing `IPython` in a case where we wouldn't need it (#3748) - Avoid importing `IPython` in a case where we wouldn't need it (#3748)
### Output ### Output

View File

@ -276,15 +276,24 @@ def normalize_path_maybe_ignore(
return root_relative_path return root_relative_path
def path_is_ignored( def _path_is_ignored(
path: Path, gitignore_dict: Dict[Path, PathSpec], report: Report root_relative_path: str,
root: Path,
gitignore_dict: Dict[Path, PathSpec],
report: Report,
) -> bool: ) -> bool:
path = root / root_relative_path
# Note that this logic is sensitive to the ordering of gitignore_dict. Callers must
# ensure that gitignore_dict is ordered from least specific to most specific.
for gitignore_path, pattern in gitignore_dict.items(): for gitignore_path, pattern in gitignore_dict.items():
relative_path = normalize_path_maybe_ignore(path, gitignore_path, report) try:
if relative_path is None: relative_path = path.relative_to(gitignore_path).as_posix()
except ValueError:
break break
if pattern.match_file(relative_path): if pattern.match_file(relative_path):
report.path_ignored(path, "matches a .gitignore file content") report.path_ignored(
path.relative_to(root), "matches a .gitignore file content"
)
return True return True
return False return False
@ -326,7 +335,9 @@ def gen_python_files(
continue continue
# First ignore files matching .gitignore, if passed # First ignore files matching .gitignore, if passed
if gitignore_dict and path_is_ignored(child, gitignore_dict, report): if gitignore_dict and _path_is_ignored(
normalized_path, root, gitignore_dict, report
):
continue continue
# Then ignore with `--exclude` `--extend-exclude` and `--force-exclude` options. # Then ignore with `--exclude` `--extend-exclude` and `--force-exclude` options.

View File

@ -508,6 +508,8 @@ def _mocked_calls() -> bool:
"pathlib.Path.cwd", return_value=working_directory "pathlib.Path.cwd", return_value=working_directory
), patch("pathlib.Path.is_dir", side_effect=mock_n_calls([True])): ), patch("pathlib.Path.is_dir", side_effect=mock_n_calls([True])):
ctx = FakeContext() ctx = FakeContext()
# Note that the root folder (project_root) isn't the folder
# named "root" (aka working_directory)
ctx.obj["root"] = project_root ctx.obj["root"] = project_root
report = MagicMock(verbose=True) report = MagicMock(verbose=True)
black.get_sources( black.get_sources(
@ -527,7 +529,7 @@ def _mocked_calls() -> bool:
for _, mock_args, _ in report.path_ignored.mock_calls for _, mock_args, _ in report.path_ignored.mock_calls
), "A symbolic link was reported." ), "A symbolic link was reported."
report.path_ignored.assert_called_once_with( report.path_ignored.assert_called_once_with(
Path("child", "b.py"), "matches a .gitignore file content" Path("root", "child", "b.py"), "matches a .gitignore file content"
) )
def test_report_verbose(self) -> None: def test_report_verbose(self) -> None: