Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

### Highlights

- Improve parse error readability by showing multi-line output with an error pointer.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be in the "output" section

(#5068)

<!-- Include any especially major or disruptive changes here -->

### Stable style
Expand Down
15 changes: 12 additions & 3 deletions docs/usage_and_configuration/the_basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +356 to +359
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not the actual error message, it should be:

Suggested change
error: cannot format src/black_primer/cli.py: Cannot parse: 5:6
import asyncio
^
ParseError: invalid syntax
error: cannot format src/black_primer/cli.py: Cannot parse: 5:6
mport asyncio
^
ParseError: bad input

Applies to the other two as well

```

#### `-v`, `--verbose`
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
18 changes: 14 additions & 4 deletions src/black/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,27 @@ def lib2to3_parse(
faulty_line = lines[lineno - 1]
except IndexError:
faulty_line = "<line number missing in source>"
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"
f"ParseError: {pe.msg}"
)

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(
f"Cannot parse{tv_str}: {lineno}:{column}: {te.args[0]}"
error_msg = (
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)

else:
# Choose the latest version when raising the actual parsing error.
assert len(errors) >= 1
Expand Down
16 changes: 13 additions & 3 deletions tests/test_black.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This call was deindented a level, that's not correct

"Cannot parse: 1:6\n"
" Unexpected EOF in multi-line statement\n"
" ^\n"
"TokenError: Unexpected EOF in multi-line statement"
)
Comment on lines +1993 to +1998
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lines got unindented which isn't correct


def test_line_ranges_with_code_option(self) -> None:
code = textwrap.dedent("""\
Expand Down
5 changes: 4 additions & 1 deletion tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Comment on lines +92 to +95
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test fails - the code should be indented another level

)
Loading