Remove newline after code block open (#3035)

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
Sagi Shadur 2022-06-11 09:55:01 +03:00 committed by GitHub
parent 6d32ab02c5
commit 4bb7bf2bdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 271 additions and 1 deletions

View File

@ -148,6 +148,7 @@ Multiple contributions by:
- [Rishikesh Jha](mailto:rishijha424@gmail.com) - [Rishikesh Jha](mailto:rishijha424@gmail.com)
- [Rupert Bedford](mailto:rupert@rupertb.com) - [Rupert Bedford](mailto:rupert@rupertb.com)
- Russell Davis - Russell Davis
- [Sagi Shadur](mailto:saroad2@gmail.com)
- [Rémi Verschelde](mailto:rverschelde@gmail.com) - [Rémi Verschelde](mailto:rverschelde@gmail.com)
- [Sami Salonen](mailto:sakki@iki.fi) - [Sami Salonen](mailto:sakki@iki.fi)
- [Samuel Cormier-Iijima](mailto:samuel@cormier-iijima.com) - [Samuel Cormier-Iijima](mailto:samuel@cormier-iijima.com)

View File

@ -21,6 +21,7 @@
- Remove redundant parentheses around awaited objects (#2991) - Remove redundant parentheses around awaited objects (#2991)
- Parentheses around return annotations are now managed (#2990) - Parentheses around return annotations are now managed (#2990)
- Remove unnecessary parentheses from `with` statements (#2926) - Remove unnecessary parentheses from `with` statements (#2926)
- Remove trailing newlines after code block open (#3035)
### _Blackd_ ### _Blackd_

View File

@ -49,3 +49,28 @@ plain strings. User-made splits are respected when they do not exceed the line l
limit. Line continuation backslashes are converted into parenthesized strings. limit. Line continuation backslashes are converted into parenthesized strings.
Unnecessary parentheses are stripped. The stability and status of this feature is Unnecessary parentheses are stripped. The stability and status of this feature is
tracked in [this issue](https://github.com/psf/black/issues/2188). tracked in [this issue](https://github.com/psf/black/issues/2188).
### Removing trailing newlines after code block open
_Black_ will remove trailing newlines after code block openings. That means that the
following code:
```python
def my_func():
print("The line above me will be deleted!")
print("But the line above me won't!")
```
Will be changed to:
```python
def my_func():
print("The line above me will be deleted!")
print("But the line above me won't!")
```
This new feature will be applied to **all code blocks**: `def`, `class`, `if`, `for`,
`while`, `with`, `case` and `match`.

View File

@ -168,6 +168,13 @@ def is_triple_quoted_string(self) -> bool:
and self.leaves[0].value.startswith(('"""', "'''")) and self.leaves[0].value.startswith(('"""', "'''"))
) )
@property
def opens_block(self) -> bool:
"""Does this line open a new level of indentation."""
if len(self.leaves) == 0:
return False
return self.leaves[-1].type == token.COLON
def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool: def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool:
"""If so, needs to be split before emitting.""" """If so, needs to be split before emitting."""
for leaf in self.leaves: for leaf in self.leaves:
@ -513,6 +520,12 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
): ):
return before, 1 return before, 1
if (
Preview.remove_block_trailing_newline in current_line.mode
and self.previous_line
and self.previous_line.opens_block
):
return 0, 0
return before, 0 return before, 0
def _maybe_empty_lines_for_class_or_def( def _maybe_empty_lines_for_class_or_def(

View File

@ -150,6 +150,7 @@ class Preview(Enum):
one_element_subscript = auto() one_element_subscript = auto()
annotation_parens = auto() annotation_parens = auto()
long_docstring_quotes_on_newline = auto() long_docstring_quotes_on_newline = auto()
remove_block_trailing_newline = auto()
class Deprecated(UserWarning): class Deprecated(UserWarning):

View File

@ -0,0 +1,189 @@
import random
def foo1():
print("The newline above me should be deleted!")
def foo2():
print("All the newlines above me should be deleted!")
def foo3():
print("No newline above me!")
print("There is a newline above me, and that's OK!")
def foo4():
# There is a comment here
print("The newline above me should not be deleted!")
class Foo:
def bar(self):
print("The newline above me should be deleted!")
for i in range(5):
print(f"{i}) The line above me should be removed!")
for i in range(5):
print(f"{i}) The lines above me should be removed!")
for i in range(5):
for j in range(7):
print(f"{i}) The lines above me should be removed!")
if random.randint(0, 3) == 0:
print("The new line above me is about to be removed!")
if random.randint(0, 3) == 0:
print("The new lines above me is about to be removed!")
if random.randint(0, 3) == 0:
if random.uniform(0, 1) > 0.5:
print("Two lines above me are about to be removed!")
while True:
print("The newline above me should be deleted!")
while True:
print("The newlines above me should be deleted!")
while True:
while False:
print("The newlines above me should be deleted!")
with open("/path/to/file.txt", mode="w") as file:
file.write("The new line above me is about to be removed!")
with open("/path/to/file.txt", mode="w") as file:
file.write("The new lines above me is about to be removed!")
with open("/path/to/file.txt", mode="r") as read_file:
with open("/path/to/output_file.txt", mode="w") as write_file:
write_file.writelines(read_file.readlines())
# output
import random
def foo1():
print("The newline above me should be deleted!")
def foo2():
print("All the newlines above me should be deleted!")
def foo3():
print("No newline above me!")
print("There is a newline above me, and that's OK!")
def foo4():
# There is a comment here
print("The newline above me should not be deleted!")
class Foo:
def bar(self):
print("The newline above me should be deleted!")
for i in range(5):
print(f"{i}) The line above me should be removed!")
for i in range(5):
print(f"{i}) The lines above me should be removed!")
for i in range(5):
for j in range(7):
print(f"{i}) The lines above me should be removed!")
if random.randint(0, 3) == 0:
print("The new line above me is about to be removed!")
if random.randint(0, 3) == 0:
print("The new lines above me is about to be removed!")
if random.randint(0, 3) == 0:
if random.uniform(0, 1) > 0.5:
print("Two lines above me are about to be removed!")
while True:
print("The newline above me should be deleted!")
while True:
print("The newlines above me should be deleted!")
while True:
while False:
print("The newlines above me should be deleted!")
with open("/path/to/file.txt", mode="w") as file:
file.write("The new line above me is about to be removed!")
with open("/path/to/file.txt", mode="w") as file:
file.write("The new lines above me is about to be removed!")
with open("/path/to/file.txt", mode="r") as read_file:
with open("/path/to/output_file.txt", mode="w") as write_file:
write_file.writelines(read_file.readlines())

View File

@ -0,0 +1,34 @@
def http_status(status):
match status:
case 400:
return "Bad request"
case 401:
return "Unauthorized"
case 403:
return "Forbidden"
case 404:
return "Not found"
# output
def http_status(status):
match status:
case 400:
return "Bad request"
case 401:
return "Unauthorized"
case 403:
return "Forbidden"
case 404:
return "Not found"

View File

@ -1461,7 +1461,6 @@ def test_newline_comment_interaction(self) -> None:
black.assert_stable(source, output, mode=DEFAULT_MODE) black.assert_stable(source, output, mode=DEFAULT_MODE)
def test_bpo_2142_workaround(self) -> None: def test_bpo_2142_workaround(self) -> None:
# https://bugs.python.org/issue2142 # https://bugs.python.org/issue2142
source, _ = read_data("miscellaneous", "missing_final_newline") source, _ = read_data("miscellaneous", "missing_final_newline")

View File

@ -86,6 +86,13 @@ def test_preview_minimum_python_39_format(filename: str) -> None:
assert_format(source, expected, mode, minimum_version=(3, 9)) assert_format(source, expected, mode, minimum_version=(3, 9))
@pytest.mark.parametrize("filename", all_data_cases("preview_310"))
def test_preview_minimum_python_310_format(filename: str) -> None:
source, expected = read_data("preview_310", filename)
mode = black.Mode(preview=True)
assert_format(source, expected, mode, minimum_version=(3, 10))
@pytest.mark.parametrize("filename", SOURCES) @pytest.mark.parametrize("filename", SOURCES)
def test_source_is_formatted(filename: str) -> None: def test_source_is_formatted(filename: str) -> None:
check_file("", filename, DEFAULT_MODE, data=False) check_file("", filename, DEFAULT_MODE, data=False)