Remove newline after code block open (#3035)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
parent
6d32ab02c5
commit
4bb7bf2bdc
@ -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)
|
||||||
|
@ -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_
|
||||||
|
|
||||||
|
@ -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`.
|
||||||
|
@ -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(
|
||||||
|
@ -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):
|
||||||
|
189
tests/data/preview/remove_newline_after_code_block_open.py
Normal file
189
tests/data/preview/remove_newline_after_code_block_open.py
Normal 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())
|
34
tests/data/preview_310/remove_newline_after match.py
Normal file
34
tests/data/preview_310/remove_newline_after match.py
Normal 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"
|
@ -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")
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user