Support files with type comment syntax errors (#3594)

This commit is contained in:
Tushar Sadhwani 2023-03-20 04:22:06 +05:30 committed by GitHub
parent dba3c2695c
commit 53c23e62df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 7 deletions

View File

@ -38,6 +38,8 @@
<!-- Changes to the parser or to version autodetection -->
- Added support for formatting files with invalid type comments (#3594)
### Performance
<!-- Changes that improve Black's performance. -->

View File

@ -148,24 +148,29 @@ def lib2to3_unparse(node: Node) -> str:
def parse_single_version(
src: str, version: Tuple[int, int]
src: str, version: Tuple[int, int], *, type_comments: bool
) -> Union[ast.AST, ast3.AST]:
filename = "<unknown>"
# typed-ast is needed because of feature version limitations in the builtin ast 3.8>
if sys.version_info >= (3, 8) and version >= (3,):
return ast.parse(src, filename, feature_version=version, type_comments=True)
return ast.parse(
src, filename, feature_version=version, type_comments=type_comments
)
if _IS_PYPY:
# PyPy 3.7 doesn't support type comment tracking which is not ideal, but there's
# not much we can do as typed-ast won't work either.
if sys.version_info >= (3, 8):
return ast3.parse(src, filename, type_comments=True)
return ast3.parse(src, filename, type_comments=type_comments)
else:
return ast3.parse(src, filename)
else:
# Typed-ast is guaranteed to be used here and automatically tracks type
# comments separately.
return ast3.parse(src, filename, feature_version=version[1])
if type_comments:
# Typed-ast is guaranteed to be used here and automatically tracks type
# comments separately.
return ast3.parse(src, filename, feature_version=version[1])
else:
return ast.parse(src, filename)
def parse_ast(src: str) -> Union[ast.AST, ast3.AST]:
@ -175,11 +180,18 @@ def parse_ast(src: str) -> Union[ast.AST, ast3.AST]:
first_error = ""
for version in sorted(versions, reverse=True):
try:
return parse_single_version(src, version)
return parse_single_version(src, version, type_comments=True)
except SyntaxError as e:
if not first_error:
first_error = str(e)
# Try to parse without type comments
for version in sorted(versions, reverse=True):
try:
return parse_single_version(src, version, type_comments=False)
except SyntaxError:
pass
raise SyntaxError(first_error)

View File

@ -0,0 +1,11 @@
def foo(
# type: Foo
x): pass
# output
def foo(
# type: Foo
x,
):
pass

View File

@ -196,3 +196,10 @@ def test_power_op_newline() -> None:
# requires line_length=0
source, expected = read_data("miscellaneous", "power_op_newline")
assert_format(source, expected, mode=black.Mode(line_length=0))
def test_type_comment_syntax_error() -> None:
"""Test that black is able to format python code with type comment syntax errors."""
source, expected = read_data("type_comments", "type_comment_syntax_error")
assert_format(source, expected)
black.assert_equivalent(source, expected)