From 5d8d6e837f5bba4c51d8ff9349005a0763d37605 Mon Sep 17 00:00:00 2001 From: Chandana098-learn Date: Sun, 29 Mar 2026 21:18:18 +0530 Subject: [PATCH 1/7] Improve parse error readability --- src/black/parsing.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/black/parsing.py b/src/black/parsing.py index cb4f715dcb3..2215aafb554 100644 --- a/src/black/parsing.py +++ b/src/black/parsing.py @@ -81,14 +81,24 @@ def lib2to3_parse( except IndexError: faulty_line = "" errors[grammar.version] = InvalidInput( - f"Cannot parse{tv_str}: {lineno}:{column}: {faulty_line}" + error_msg = ( + f"Cannot parse{tv_str}:{lineno}:{column}\n" + f" {faulty_line}\n" + f" {' ' * (column - 1)}^\n" + "SyntaxError: invalid syntax" + ) ) except TokenError as te: # In edge cases these are raised; and typically don't have a "faulty_line". lineno, column = te.args[1] errors[grammar.version] = InvalidInput( - f"Cannot parse{tv_str}: {lineno}:{column}: {te.args[0]}" + error_msg = ( + f"Cannot parse{tv_str}:{lineno}:{column}\n" + f" {faulty_line}\n" + f" {' ' * (column - 1)}^\n" + "SyntaxError: invalid syntax" + ) ) else: From d8ae509635f9074660af241947d309279d77cef5 Mon Sep 17 00:00:00 2001 From: Chandana098-learn Date: Sun, 29 Mar 2026 21:53:06 +0530 Subject: [PATCH 2/7] Fix flake8 and mypy issues in parse error formatting --- src/black/parsing.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/black/parsing.py b/src/black/parsing.py index 2215aafb554..23f43c0dcae 100644 --- a/src/black/parsing.py +++ b/src/black/parsing.py @@ -80,26 +80,26 @@ def lib2to3_parse( faulty_line = lines[lineno - 1] except IndexError: faulty_line = "" - errors[grammar.version] = InvalidInput( - error_msg = ( - f"Cannot parse{tv_str}:{lineno}:{column}\n" - f" {faulty_line}\n" - f" {' ' * (column - 1)}^\n" - "SyntaxError: invalid syntax" - ) - ) + error_msg = ( + f"Cannot parse{tv_str}: {lineno}:{column}\n" + f" {faulty_line}\n" + f" {' ' * (column - 1)}^\n" + "SyntaxError: invalid syntax" + ) + + errors[grammar.version] = InvalidInput(error_msg) except TokenError as te: # In edge cases these are raised; and typically don't have a "faulty_line". lineno, column = te.args[1] - errors[grammar.version] = InvalidInput( - error_msg = ( - f"Cannot parse{tv_str}:{lineno}:{column}\n" - f" {faulty_line}\n" - f" {' ' * (column - 1)}^\n" - "SyntaxError: invalid syntax" - ) - ) + error_msg = ( + f"Cannot parse{tv_str}: {lineno}:{column}\n" + f" {te.args[0]}\n" + f" {' ' * (column - 1)}^\n" + "SyntaxError: invalid syntax" + ) + + errors[grammar.version] = InvalidInput(error_msg) else: # Choose the latest version when raising the actual parsing error. From 231d7d95197c037b9044288f806f349f428320de Mon Sep 17 00:00:00 2001 From: Chandana098-learn Date: Sun, 29 Mar 2026 23:12:03 +0530 Subject: [PATCH 3/7] Improve ParseError and TokenError messages --- src/black/parsing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/black/parsing.py b/src/black/parsing.py index 23f43c0dcae..658b5bcb933 100644 --- a/src/black/parsing.py +++ b/src/black/parsing.py @@ -84,7 +84,7 @@ def lib2to3_parse( f"Cannot parse{tv_str}: {lineno}:{column}\n" f" {faulty_line}\n" f" {' ' * (column - 1)}^\n" - "SyntaxError: invalid syntax" + f"ParseError: {pe.msg}" ) errors[grammar.version] = InvalidInput(error_msg) @@ -96,7 +96,7 @@ def lib2to3_parse( f"Cannot parse{tv_str}: {lineno}:{column}\n" f" {te.args[0]}\n" f" {' ' * (column - 1)}^\n" - "SyntaxError: invalid syntax" + f"TokenError: {te.args[0]}" ) errors[grammar.version] = InvalidInput(error_msg) From 18b58a275d81dc74799614cd7585b59356a6d055 Mon Sep 17 00:00:00 2001 From: Chandana098-learn Date: Sun, 29 Mar 2026 23:17:21 +0530 Subject: [PATCH 4/7] Add changelog entry --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index b326a0139d1..4fb80c29b23 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ Please include the PR number in the changelog entry, not the issue number --> ### Highlights +- Improve parse error readability by showing multi-line output with an error pointer. (#5068) From d31be90658db4a2d86ccf8f16820fb80ad568a07 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 29 Mar 2026 17:47:44 +0000 Subject: [PATCH 5/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CHANGES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 4fb80c29b23..bab9c24c84e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,9 @@ Please include the PR number in the changelog entry, not the issue number --> ### Highlights -- Improve parse error readability by showing multi-line output with an error pointer. (#5068) + +- Improve parse error readability by showing multi-line output with an error pointer. + (#5068) From 08f2a57fcdf838e6c4f742980ff1a1529c45029b Mon Sep 17 00:00:00 2001 From: Chandana098-learn Date: Sun, 29 Mar 2026 23:27:08 +0530 Subject: [PATCH 6/7] Format parsing.py with Black --- src/black/parsing.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/black/parsing.py b/src/black/parsing.py index 658b5bcb933..1203426d6fc 100644 --- a/src/black/parsing.py +++ b/src/black/parsing.py @@ -81,11 +81,11 @@ def lib2to3_parse( except IndexError: faulty_line = "" error_msg = ( - f"Cannot parse{tv_str}: {lineno}:{column}\n" - f" {faulty_line}\n" - f" {' ' * (column - 1)}^\n" - f"ParseError: {pe.msg}" - ) + f"Cannot parse{tv_str}: {lineno}:{column}\n" + f" {faulty_line}\n" + f" {' ' * (column - 1)}^\n" + f"ParseError: {pe.msg}" + ) errors[grammar.version] = InvalidInput(error_msg) @@ -93,11 +93,11 @@ def lib2to3_parse( # In edge cases these are raised; and typically don't have a "faulty_line". lineno, column = te.args[1] error_msg = ( - f"Cannot parse{tv_str}: {lineno}:{column}\n" - f" {te.args[0]}\n" - f" {' ' * (column - 1)}^\n" - f"TokenError: {te.args[0]}" - ) + f"Cannot parse{tv_str}: {lineno}:{column}\n" + f" {te.args[0]}\n" + f" {' ' * (column - 1)}^\n" + f"TokenError: {te.args[0]}" + ) errors[grammar.version] = InvalidInput(error_msg) From 2550fe80f6beaa4989860c19d6ed1d8ebc59cb9e Mon Sep 17 00:00:00 2001 From: Chandana098-learn Date: Mon, 30 Mar 2026 21:27:25 +0530 Subject: [PATCH 7/7] Update docs and tests for new error message format --- docs/usage_and_configuration/the_basics.md | 15 ++++++++++++--- tests/test_black.py | 16 +++++++++++++--- tests/test_format.py | 5 ++++- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/docs/usage_and_configuration/the_basics.md b/docs/usage_and_configuration/the_basics.md index 738a9793bbb..7cfefdb0657 100644 --- a/docs/usage_and_configuration/the_basics.md +++ b/docs/usage_and_configuration/the_basics.md @@ -353,7 +353,10 @@ silenced by `2>/dev/null`). ```console $ black src/ -q -error: cannot format src/black_primer/cli.py: Cannot parse: 5:6: mport asyncio +error: cannot format src/black_primer/cli.py: Cannot parse: 5:6 + import asyncio + ^ +ParseError: invalid syntax ``` #### `-v`, `--verbose` @@ -368,7 +371,10 @@ Using configuration from /tmp/pyproject.toml. src/blib2to3 ignored: matches the --extend-exclude regular expression src/_black_version.py wasn't modified on disk since last run. src/black/__main__.py wasn't modified on disk since last run. -error: cannot format src/black_primer/cli.py: Cannot parse: 5:6: mport asyncio +error: cannot format src/black_primer/cli.py: Cannot parse: 5:6 + mport asyncio + ^ +ParseError: invalid syntax reformatted src/black_primer/lib.py reformatted src/blackd/__init__.py reformatted src/black/__init__.py @@ -443,7 +449,10 @@ plus a short summary. ```console $ black src/ -error: cannot format src/black_primer/cli.py: Cannot parse: 5:6: mport asyncio +error: cannot format src/black_primer/cli.py: Cannot parse: 5:6 + mport asyncio + ^ +ParseError: invalid syntax reformatted src/black_primer/lib.py reformatted src/blackd/__init__.py reformatted src/black/__init__.py diff --git a/tests/test_black.py b/tests/test_black.py index 862679feef4..5ecf10702cc 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -1025,8 +1025,13 @@ def test_format_file_contents(self) -> None: invalid = "return if you can" with self.assertRaises(black.InvalidInput) as e: black.format_file_contents(invalid, mode=mode, fast=False) - self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can") - + self.assertEqual( + str(e.exception), + "Cannot parse: 1:7\n" + " return if you can\n" + " ^\n" + "ParseError: invalid syntax", + ) just_crlf = "\r\n" with self.assertRaises(black.NothingChanged): black.format_file_contents(just_crlf, mode=mode, fast=False) @@ -1985,7 +1990,12 @@ def test_for_handled_unexpected_eof_error(self) -> None: with pytest.raises(black.parsing.InvalidInput) as exc_info: black.lib2to3_parse("print(", {}) - exc_info.match("Cannot parse: 1:6: Unexpected EOF in multi-line statement") + exc_info.match( + "Cannot parse: 1:6\n" + " Unexpected EOF in multi-line statement\n" + " ^\n" + "TokenError: Unexpected EOF in multi-line statement" + ) def test_line_ranges_with_code_option(self) -> None: code = textwrap.dedent("""\ diff --git a/tests/test_format.py b/tests/test_format.py index 31c44b9fa90..14704ec1525 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -89,5 +89,8 @@ def test_patma_invalid() -> None: assert_format(source, expected, mode, minimum_version=(3, 10)) exc_info.match( - "Cannot parse for target version Python 3.10: 10:11: case a := b:" + "Cannot parse for target version Python 3.10: 10:11\n" + " case a := b:\n" + " ^\n" + "ParseError: invalid syntax" )