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:
parent
ef7c45f281
commit
104aec555f
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
- Add support for formatting Jupyter Notebook files (#2357)
|
- Add support for formatting Jupyter Notebook files (#2357)
|
||||||
- Move from `appdirs` dependency to `platformdirs` (#2375)
|
- Move from `appdirs` dependency to `platformdirs` (#2375)
|
||||||
|
- Present a more user-friendly error if .gitignore is invalid (#2414)
|
||||||
|
|
||||||
### Integrations
|
### Integrations
|
||||||
|
|
||||||
|
2
setup.py
2
setup.py
@ -77,7 +77,7 @@ def get_long_description() -> str:
|
|||||||
"tomli>=0.2.6,<2.0.0",
|
"tomli>=0.2.6,<2.0.0",
|
||||||
"typed-ast>=1.4.2; python_version < '3.8'",
|
"typed-ast>=1.4.2; python_version < '3.8'",
|
||||||
"regex>=2020.1.8",
|
"regex>=2020.1.8",
|
||||||
"pathspec>=0.8.1, <1",
|
"pathspec>=0.9.0, <1",
|
||||||
"dataclasses>=0.6; python_version < '3.7'",
|
"dataclasses>=0.6; python_version < '3.7'",
|
||||||
"typing_extensions>=3.10.0.0; python_version < '3.10'",
|
"typing_extensions>=3.10.0.0; python_version < '3.10'",
|
||||||
"mypy_extensions>=0.4.3",
|
"mypy_extensions>=0.4.3",
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
from multiprocessing import Manager, freeze_support
|
from multiprocessing import Manager, freeze_support
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from pathspec.patterns.gitwildmatch import GitWildMatchPatternError
|
||||||
import regex as re
|
import regex as re
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
@ -428,18 +429,21 @@ def main(
|
|||||||
content=code, fast=fast, write_back=write_back, mode=mode, report=report
|
content=code, fast=fast, write_back=write_back, mode=mode, report=report
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
sources = get_sources(
|
try:
|
||||||
ctx=ctx,
|
sources = get_sources(
|
||||||
src=src,
|
ctx=ctx,
|
||||||
quiet=quiet,
|
src=src,
|
||||||
verbose=verbose,
|
quiet=quiet,
|
||||||
include=include,
|
verbose=verbose,
|
||||||
exclude=exclude,
|
include=include,
|
||||||
extend_exclude=extend_exclude,
|
exclude=exclude,
|
||||||
force_exclude=force_exclude,
|
extend_exclude=extend_exclude,
|
||||||
report=report,
|
force_exclude=force_exclude,
|
||||||
stdin_filename=stdin_filename,
|
report=report,
|
||||||
)
|
stdin_filename=stdin_filename,
|
||||||
|
)
|
||||||
|
except GitWildMatchPatternError:
|
||||||
|
ctx.exit(1)
|
||||||
|
|
||||||
path_empty(
|
path_empty(
|
||||||
sources,
|
sources,
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
from pathspec import PathSpec
|
from pathspec import PathSpec
|
||||||
|
from pathspec.patterns.gitwildmatch import GitWildMatchPatternError
|
||||||
import tomli
|
import tomli
|
||||||
|
|
||||||
from black.output import err
|
from black.output import err
|
||||||
@ -122,7 +123,11 @@ def get_gitignore(root: Path) -> PathSpec:
|
|||||||
if gitignore.is_file():
|
if gitignore.is_file():
|
||||||
with gitignore.open(encoding="utf-8") as gf:
|
with gitignore.open(encoding="utf-8") as gf:
|
||||||
lines = gf.readlines()
|
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(
|
def normalize_path_maybe_ignore(
|
||||||
|
1
tests/data/invalid_gitignore_tests/.gitignore
vendored
Normal file
1
tests/data/invalid_gitignore_tests/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
!
|
0
tests/data/invalid_gitignore_tests/a.py
Normal file
0
tests/data/invalid_gitignore_tests/a.py
Normal file
1
tests/data/invalid_gitignore_tests/pyproject.toml
Normal file
1
tests/data/invalid_gitignore_tests/pyproject.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Empty configuration file; used in tests to avoid interference from Black's own config.
|
0
tests/data/invalid_nested_gitignore_tests/a.py
Normal file
0
tests/data/invalid_nested_gitignore_tests/a.py
Normal file
1
tests/data/invalid_nested_gitignore_tests/a/.gitignore
vendored
Normal file
1
tests/data/invalid_nested_gitignore_tests/a/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
!
|
0
tests/data/invalid_nested_gitignore_tests/a/a.py
Normal file
0
tests/data/invalid_nested_gitignore_tests/a/a.py
Normal file
1
tests/data/invalid_nested_gitignore_tests/pyproject.toml
Normal file
1
tests/data/invalid_nested_gitignore_tests/pyproject.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Empty configuration file; used in tests to avoid interference from Black's own config.
|
@ -1727,6 +1727,30 @@ def test_nested_gitignore(self) -> None:
|
|||||||
)
|
)
|
||||||
self.assertEqual(sorted(expected), sorted(sources))
|
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:
|
def test_empty_include(self) -> None:
|
||||||
path = THIS_DIR / "data" / "include_exclude_tests"
|
path = THIS_DIR / "data" / "include_exclude_tests"
|
||||||
report = black.Report()
|
report = black.Report()
|
||||||
|
Loading…
Reference in New Issue
Block a user