Skip to content

Commit 9fd9ea2

Browse files
authored
Fix blackd error handling: split SourceASTParseError from ASTSafetyError (#5080)
1 parent 866c350 commit 9fd9ea2

5 files changed

Lines changed: 24 additions & 3 deletions

File tree

CHANGES.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,17 @@
5050
- Improve parse error readability by showing multi-line output with an error pointer.
5151
(#5068)
5252

53+
- Add `SourceASTParseError` to distinguish source parse failures from internal safety
54+
errors, improving error reporting when Black's lenient parser accepts input that
55+
`ast.parse()` rejects (#5080)
56+
5357
### _Blackd_
5458

5559
<!-- Changes to blackd -->
5660

61+
- Return HTTP 400 (Bad Request) for source parse failures instead of HTTP 500, keeping
62+
HTTP 500 only for genuine internal safety errors (#5080)
63+
5764
### Integrations
5865

5966
<!-- For example, Docker, GitHub Actions, pre-commit, editors -->

src/black/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
from black.parsing import ( # noqa F401
6767
ASTSafetyError,
6868
InvalidInput,
69+
SourceASTParseError,
6970
lib2to3_parse,
7071
parse_ast,
7172
stringify_ast,
@@ -1086,6 +1087,8 @@ def check_stability_and_equivalence(
10861087
"""
10871088
try:
10881089
assert_equivalent(src_contents, dst_contents)
1090+
except SourceASTParseError:
1091+
raise
10891092
except ASTSafetyError:
10901093
if _target_versions_exceed_runtime(mode.target_versions):
10911094
raise ASTSafetyError(
@@ -1632,7 +1635,7 @@ def assert_equivalent(src: str, dst: str) -> None:
16321635
try:
16331636
src_ast = parse_ast(src)
16341637
except Exception as exc:
1635-
raise ASTSafetyError(
1638+
raise SourceASTParseError(
16361639
"cannot use --safe with this file; failed to parse source file AST: "
16371640
f"{exc}\n"
16381641
"This could be caused by running Black with an older Python version "

src/black/parsing.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ class ASTSafetyError(Exception):
119119
"""Raised when Black's generated code is not equivalent to the old AST."""
120120

121121

122+
class SourceASTParseError(Exception):
123+
"""Raised when the source file cannot be parsed by ast.parse().
124+
125+
This is not a bug in Black — Black's lib2to3-based parser is more lenient
126+
than Python's ast.parse(), so it may accept code that ast.parse() rejects.
127+
In blackd, this should be reported as a 400 Bad Request.
128+
"""
129+
130+
122131
def _parse_single_version(
123132
src: str, version: tuple[int, int], *, type_comments: bool
124133
) -> ast.AST:

src/blackd/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ async def handle(
206206
return web.Response(status=204, headers=headers)
207207
except black.InvalidInput as e:
208208
return web.Response(status=400, headers=headers, text=str(e))
209+
except black.SourceASTParseError as e:
210+
return web.Response(status=400, headers=headers, text=str(e))
209211
except web.HTTPException:
210212
raise
211213
except Exception as e:

tests/test_black.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
from black.debug import DebugVisitor
3939
from black.mode import Mode, Preview
4040
from black.output import color_diff, diff
41-
from black.parsing import ASTSafetyError
41+
from black.parsing import ASTSafetyError, SourceASTParseError
4242
from black.report import Report
4343
from black.strings import lines_with_leading_tabs_expanded
4444

@@ -3224,7 +3224,7 @@ def test_assert_equivalent_fstring(self) -> None:
32243224
)
32253225

32263226
def test_equivalency_ast_parse_failure_includes_error(self) -> None:
3227-
with pytest.raises(ASTSafetyError) as err:
3227+
with pytest.raises(SourceASTParseError) as err:
32283228
black.assert_equivalent("a«»a = 1", "a«»a = 1")
32293229

32303230
err.match("--safe")

0 commit comments

Comments
 (0)