Present a more user-friendly error if .gitignore is invalid (#2414)

Fixes #2359.

This commit now makes Black exit with an user-friendly error message if a
.gitignore file couldn't be parsed -- a massive improvement over an opaque
traceback!
This commit is contained in:
Nipunn Koorapati 2021-08-20 16:54:53 -07:00 committed by GitHub
parent ef7c45f281
commit 104aec555f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 52 additions and 14 deletions

View File

@ -6,6 +6,7 @@
- Add support for formatting Jupyter Notebook files (#2357)
- Move from `appdirs` dependency to `platformdirs` (#2375)
- Present a more user-friendly error if .gitignore is invalid (#2414)
### Integrations

View File

@ -77,7 +77,7 @@ def get_long_description() -> str:
"tomli>=0.2.6,<2.0.0",
"typed-ast>=1.4.2; python_version < '3.8'",
"regex>=2020.1.8",
"pathspec>=0.8.1, <1",
"pathspec>=0.9.0, <1",
"dataclasses>=0.6; python_version < '3.7'",
"typing_extensions>=3.10.0.0; python_version < '3.10'",
"mypy_extensions>=0.4.3",

View File

@ -9,6 +9,7 @@
from multiprocessing import Manager, freeze_support
import os
from pathlib import Path
from pathspec.patterns.gitwildmatch import GitWildMatchPatternError
import regex as re
import signal
import sys
@ -428,18 +429,21 @@ def main(
content=code, fast=fast, write_back=write_back, mode=mode, report=report
)
else:
sources = get_sources(
ctx=ctx,
src=src,
quiet=quiet,
verbose=verbose,
include=include,
exclude=exclude,
extend_exclude=extend_exclude,
force_exclude=force_exclude,
report=report,
stdin_filename=stdin_filename,
)
try:
sources = get_sources(
ctx=ctx,
src=src,
quiet=quiet,
verbose=verbose,
include=include,
exclude=exclude,
extend_exclude=extend_exclude,
force_exclude=force_exclude,
report=report,
stdin_filename=stdin_filename,
)
except GitWildMatchPatternError:
ctx.exit(1)
path_empty(
sources,

View File

@ -18,6 +18,7 @@
)
from pathspec import PathSpec
from pathspec.patterns.gitwildmatch import GitWildMatchPatternError
import tomli
from black.output import err
@ -122,7 +123,11 @@ def get_gitignore(root: Path) -> PathSpec:
if gitignore.is_file():
with gitignore.open(encoding="utf-8") as gf:
lines = gf.readlines()
return PathSpec.from_lines("gitwildmatch", lines)
try:
return PathSpec.from_lines("gitwildmatch", lines)
except GitWildMatchPatternError as e:
err(f"Could not parse {gitignore}: {e}")
raise
def normalize_path_maybe_ignore(

View File

@ -0,0 +1 @@
!

View File

View File

@ -0,0 +1 @@
# Empty configuration file; used in tests to avoid interference from Black's own config.

View File

@ -0,0 +1 @@
!

View File

@ -0,0 +1 @@
# Empty configuration file; used in tests to avoid interference from Black's own config.

View File

@ -1727,6 +1727,30 @@ def test_nested_gitignore(self) -> None:
)
self.assertEqual(sorted(expected), sorted(sources))
def test_invalid_gitignore(self) -> None:
path = THIS_DIR / "data" / "invalid_gitignore_tests"
empty_config = path / "pyproject.toml"
result = BlackRunner().invoke(
black.main, ["--verbose", "--config", str(empty_config), str(path)]
)
assert result.exit_code == 1
assert result.stderr_bytes is not None
gitignore = path / ".gitignore"
assert f"Could not parse {gitignore}" in result.stderr_bytes.decode()
def test_invalid_nested_gitignore(self) -> None:
path = THIS_DIR / "data" / "invalid_nested_gitignore_tests"
empty_config = path / "pyproject.toml"
result = BlackRunner().invoke(
black.main, ["--verbose", "--config", str(empty_config), str(path)]
)
assert result.exit_code == 1
assert result.stderr_bytes is not None
gitignore = path / "a" / ".gitignore"
assert f"Could not parse {gitignore}" in result.stderr_bytes.decode()
def test_empty_include(self) -> None:
path = THIS_DIR / "data" / "include_exclude_tests"
report = black.Report()