Improve dummy_implementations preview style formatting (#9240)

This commit is contained in:
Micha Reiser 2023-12-22 12:44:14 +09:00 committed by GitHub
parent a06723da2b
commit 9cc257ee7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 159 additions and 294 deletions

View file

@ -254,3 +254,22 @@ if True:
if True:
def nested_trailing_function():
pass
def overload1(): ... # trailing comment
def overload1(a: int): ...
def overload2(): ... # trailing comment
def overload2(a: int): ...
def overload3():
...
# trailing comment
def overload3(a: int): ...
def overload4():
...
# trailing comment
def overload4(a: int): ...

View file

@ -392,9 +392,13 @@ pub(crate) fn clause_body<'a>(
impl Format<PyFormatContext<'_>> for FormatClauseBody<'_> {
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
// In stable, stubs are only collapsed in stub files, in preview this is consistently
// applied everywhere
if (f.options().source_type().is_stub() || is_dummy_implementations_enabled(f.context()))
// In stable, stubs are only collapsed in stub files, in preview stubs in functions
// or classes are collapsed too
let should_collapse_stub = f.options().source_type().is_stub()
|| (is_dummy_implementations_enabled(f.context())
&& matches!(self.kind, SuiteKind::Function | SuiteKind::Class));
if should_collapse_stub
&& contains_only_an_ellipsis(self.body, f.context().comments())
&& self.trailing_comments.is_empty()
{

View file

@ -12,7 +12,8 @@ use crate::context::{NodeLevel, TopLevelStatementPosition, WithIndentLevel, With
use crate::expression::expr_string_literal::ExprStringLiteralKind;
use crate::prelude::*;
use crate::preview::{
is_module_docstring_newlines_enabled, is_no_blank_line_before_class_docstring_enabled,
is_dummy_implementations_enabled, is_module_docstring_newlines_enabled,
is_no_blank_line_before_class_docstring_enabled,
};
use crate::statement::stmt_expr::FormatStmtExpr;
use crate::verbatim::{
@ -261,6 +262,24 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
f,
)?;
} else {
// Preserve empty lines after a stub implementation but don't insert a new one if there isn't any present in the source.
// This is useful when having multiple function overloads that should be grouped to getter by omitting new lines between them.
let is_preceding_stub_function_without_empty_line =
is_dummy_implementations_enabled(f.context())
&& following.is_function_def_stmt()
&& preceding
.as_function_def_stmt()
.is_some_and(|preceding_stub| {
contains_only_an_ellipsis(
&preceding_stub.body,
f.context().comments(),
) && lines_after_ignoring_end_of_line_trivia(
preceding_stub.end(),
f.context().source(),
) < 2
});
if !is_preceding_stub_function_without_empty_line {
match self.kind {
SuiteKind::TopLevel => {
write!(f, [empty_line(), empty_line()])?;
@ -270,6 +289,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
}
}
}
}
} else if is_import_definition(preceding)
&& (!is_import_definition(following) || following_comments.has_leading())
{

View file

@ -1,276 +0,0 @@
---
source: crates/ruff_python_formatter/tests/fixtures.rs
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_dummy_implementations.py
---
## Input
```python
from typing import NoReturn, Protocol, Union, overload
class Empty:
...
def dummy(a): ...
async def other(b): ...
@overload
def a(arg: int) -> int: ...
@overload
def a(arg: str) -> str: ...
@overload
def a(arg: object) -> NoReturn: ...
def a(arg: Union[int, str, object]) -> Union[int, str]:
if not isinstance(arg, (int, str)):
raise TypeError
return arg
class Proto(Protocol):
def foo(self, a: int) -> int:
...
def bar(self, b: str) -> str: ...
def baz(self, c: bytes) -> str:
...
def dummy_two():
...
@dummy
def dummy_three():
...
def dummy_four():
...
@overload
def b(arg: int) -> int: ...
@overload
def b(arg: str) -> str: ...
@overload
def b(arg: object) -> NoReturn: ...
def b(arg: Union[int, str, object]) -> Union[int, str]:
if not isinstance(arg, (int, str)):
raise TypeError
return arg
def has_comment():
... # still a dummy
if some_condition:
...
if already_dummy: ...
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -5,15 +5,23 @@
def dummy(a): ...
+
+
async def other(b): ...
@overload
def a(arg: int) -> int: ...
+
+
@overload
def a(arg: str) -> str: ...
+
+
@overload
def a(arg: object) -> NoReturn: ...
+
+
def a(arg: Union[int, str, object]) -> Union[int, str]:
if not isinstance(arg, (int, str)):
raise TypeError
@@ -24,10 +32,13 @@
def foo(self, a: int) -> int: ...
def bar(self, b: str) -> str: ...
+
def baz(self, c: bytes) -> str: ...
def dummy_two(): ...
+
+
@dummy
def dummy_three(): ...
@@ -41,6 +52,8 @@
@overload
def b(arg: str) -> str: ...
+
+
@overload
def b(arg: object) -> NoReturn: ...
@@ -54,8 +67,6 @@
def has_comment(): ... # still a dummy
-if some_condition:
- ...
+if some_condition: ...
-if already_dummy:
- ...
+if already_dummy: ...
```
## Ruff Output
```python
from typing import NoReturn, Protocol, Union, overload
class Empty: ...
def dummy(a): ...
async def other(b): ...
@overload
def a(arg: int) -> int: ...
@overload
def a(arg: str) -> str: ...
@overload
def a(arg: object) -> NoReturn: ...
def a(arg: Union[int, str, object]) -> Union[int, str]:
if not isinstance(arg, (int, str)):
raise TypeError
return arg
class Proto(Protocol):
def foo(self, a: int) -> int: ...
def bar(self, b: str) -> str: ...
def baz(self, c: bytes) -> str: ...
def dummy_two(): ...
@dummy
def dummy_three(): ...
def dummy_four(): ...
@overload
def b(arg: int) -> int: ...
@overload
def b(arg: str) -> str: ...
@overload
def b(arg: object) -> NoReturn: ...
def b(arg: Union[int, str, object]) -> Union[int, str]:
if not isinstance(arg, (int, str)):
raise TypeError
return arg
def has_comment(): ... # still a dummy
if some_condition: ...
if already_dummy: ...
```
## Black Output
```python
from typing import NoReturn, Protocol, Union, overload
class Empty: ...
def dummy(a): ...
async def other(b): ...
@overload
def a(arg: int) -> int: ...
@overload
def a(arg: str) -> str: ...
@overload
def a(arg: object) -> NoReturn: ...
def a(arg: Union[int, str, object]) -> Union[int, str]:
if not isinstance(arg, (int, str)):
raise TypeError
return arg
class Proto(Protocol):
def foo(self, a: int) -> int: ...
def bar(self, b: str) -> str: ...
def baz(self, c: bytes) -> str: ...
def dummy_two(): ...
@dummy
def dummy_three(): ...
def dummy_four(): ...
@overload
def b(arg: int) -> int: ...
@overload
def b(arg: str) -> str: ...
@overload
def b(arg: object) -> NoReturn: ...
def b(arg: Union[int, str, object]) -> Union[int, str]:
if not isinstance(arg, (int, str)):
raise TypeError
return arg
def has_comment(): ... # still a dummy
if some_condition:
...
if already_dummy:
...
```

View file

@ -259,7 +259,27 @@ if True:
# empty line(s) at the end of the file due to nested function
if True:
def nested_trailing_function():
pass```
pass
def overload1(): ... # trailing comment
def overload1(a: int): ...
def overload2(): ... # trailing comment
def overload2(a: int): ...
def overload3():
...
# trailing comment
def overload3(a: int): ...
def overload4():
...
# trailing comment
def overload4(a: int): ...
```
## Output
```python
@ -546,6 +566,40 @@ if True:
def nested_trailing_function():
pass
def overload1():
... # trailing comment
def overload1(a: int):
...
def overload2():
... # trailing comment
def overload2(a: int):
...
def overload3():
...
# trailing comment
def overload3(a: int):
...
def overload4():
...
# trailing comment
def overload4(a: int):
...
```
@ -569,6 +623,48 @@ if True:
def fakehttp():
@@ -283,20 +281,14 @@
pass
-def overload1():
- ... # trailing comment
+def overload1(): ... # trailing comment
+def overload1(a: int): ...
-def overload1(a: int):
- ...
+def overload2(): ... # trailing comment
-def overload2():
- ... # trailing comment
-
-
-def overload2(a: int):
- ...
+def overload2(a: int): ...
def overload3():
@@ -304,8 +296,7 @@
# trailing comment
-def overload3(a: int):
- ...
+def overload3(a: int): ...
def overload4():
@@ -313,5 +304,4 @@
# trailing comment
-def overload4(a: int):
- ...
+def overload4(a: int): ...
```

View file

@ -150,21 +150,23 @@ def quuz():
class Baz(Qux):
@@ -49,12 +44,10 @@
@@ -49,14 +44,8 @@
pass
-def bar():
- ...
+def bar(): ...
-
-
-def baz():
- ...
-
-
+def bar(): ...
+def baz(): ...
def quux():
"""Some docstring."""
```