Skip to content

Commit

Permalink
Provide a more useful error when parsing fails during AST safety chec…
Browse files Browse the repository at this point in the history
…ks (psf#2218)
  • Loading branch information
hramezani authored and felix-hilden committed Jun 3, 2021
1 parent f2a3fee commit 4d9dd8f
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -51,6 +51,7 @@
### _Black_

- Refactor `src/black/__init__.py` into many files (#2206)
- Provide a more useful error when parsing fails during AST safety checks (#2218)

### Documentation

Expand Down
14 changes: 11 additions & 3 deletions src/black/parsing.py
Expand Up @@ -108,26 +108,34 @@ def lib2to3_unparse(node: Node) -> str:

def parse_ast(src: str) -> Union[ast.AST, ast3.AST, ast27.AST]:
filename = "<unknown>"
error = None
if sys.version_info >= (3, 8):
# TODO: support Python 4+ ;)
for minor_version in range(sys.version_info[1], 4, -1):
try:
return ast.parse(src, filename, feature_version=(3, minor_version))
except SyntaxError:
except SyntaxError as e:
if error is None:
error = str(e)
continue
else:
for feature_version in (7, 6):
try:
return ast3.parse(src, filename, feature_version=feature_version)
except SyntaxError:
except SyntaxError as e:
if error is None:
error = str(e)
continue
if ast27.__name__ == "ast":
raise SyntaxError(
"The requested source code has invalid Python 3 syntax.\n"
"If you are trying to format Python 2 files please reinstall Black"
" with the 'python2' extra: `python3 -m pip install black[python2]`."
)
return ast27.parse(src)
try:
return ast27.parse(src)
except SyntaxError as e:
raise SyntaxError(error or str(e))


def stringify_ast(
Expand Down
23 changes: 23 additions & 0 deletions tests/test_black.py
Expand Up @@ -479,6 +479,29 @@ def test_python2_should_fail_without_optional_install(self) -> None:
)
self.assertIn(msg, actual)

@pytest.mark.python2
def test_safety_check_syntax_error(self) -> None:
source = "e = 'test'\nx = f'{'"
tmp_file = Path(black.dump_to_file(source))
try:
runner = BlackRunner()
result = runner.invoke(black.main, [str(tmp_file)])
self.assertEqual(result.exit_code, 123)
finally:
os.unlink(tmp_file)
actual = (
runner.stderr_bytes.decode()
.replace("\n", "")
.replace("\\n", "")
.replace("\\r", "")
.replace("\r", "")
)
msg = (
"cannot use --safe with this file; failed to parse source file."
" AST error message: f-string: expecting '}' (<unknown>, line 2)"
)
self.assertIn(msg, actual)

@pytest.mark.python2
@patch("black.dump_to_file", dump_to_stderr)
def test_python2_print_function(self) -> None:
Expand Down

0 comments on commit 4d9dd8f

Please sign in to comment.