Move deviations from formatter README to documentation (#10444)

## Summary

#10151 documented the deviations between Ruff and Black with the new
2024 style guide in the `ruff-python-formatter/README.md`. However,
that's not the documentation shown
on the website when navigating to [intentional
deviations](https://docs.astral.sh/ruff/formatter/black/).

This PR streamlines the `ruff-python-formatter/README.md` and links to
the documentation on the website instead of repeating the same content.
The PR also makes the 2024 style guide deviations available on the
website documentation.

## Test Plan

I built the documentation locally and verified that the 2024 style guide
known deviations are now shown on the website.
This commit is contained in:
Micha Reiser 2024-03-18 08:22:28 +00:00 committed by GitHub
parent 91e81413db
commit 12486315fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 164 additions and 761 deletions

View file

@ -71,10 +71,166 @@ on both `first()` and `second()`:
### Line width vs. line length
Ruff uses the Unicode width of a line to determine if a line fits. Black's stable style uses
character width, while Black's preview style uses Unicode width for strings ([#3445](https://github.com/psf/black/pull/3445)),
and character width for all other tokens. Ruff's behavior is closer to Black's preview style than
Black's stable style, although Ruff _also_ uses Unicode width for identifiers and comments.
Ruff uses the Unicode width of a line to determine if a line fits. Black uses Unicode width for strings,
and character width for all other tokens. Ruff _also_ uses Unicode width for identifiers and comments.
### Parenthesizing long nested-expressions
Black 24 and newer parenthesizes long conditional expressions and type annotations in function parameters:
```python
# Black
[
"____________________________",
"foo",
"bar",
(
"baz"
if some_really_looooooooong_variable
else "some other looooooooooooooong value"
),
]
def foo(
i: int,
x: (
Loooooooooooooooooooooooong
| Looooooooooooooooong
| Looooooooooooooooooooong
| Looooooong
),
*,
s: str,
) -> None:
pass
# Ruff
[
"____________________________",
"foo",
"bar",
"baz" if some_really_looooooooong_variable else "some other looooooooooooooong value"
]
def foo(
i: int,
x: Loooooooooooooooooooooooong
| Looooooooooooooooong
| Looooooooooooooooooooong
| Looooooong,
*,
s: str,
) -> None:
pass
```
We agree that Ruff's formatting (that matches Black's 23) is hard to read and needs improvement. But we aren't convinced that parenthesizing long nested expressions is the best solution, especially when considering expression formatting holistically. That's why we want to defer the decision until we've explored alternative nested expression formatting styles. See [psf/Black#4123](https://github.com/psf/black/issues/4123) for an in-depth explanation of our concerns and an outline of possible alternatives.
### Call expressions with a single multiline string argument
Unlike Black, Ruff preserves the indentation of a single multiline-string argument in a call expression:
```python
# Input
call(
""""
A multiline
string
"""
)
dedent(""""
A multiline
string
""")
# Black
call(
""""
A multiline
string
"""
)
dedent(
""""
A multiline
string
"""
)
# Ruff
call(
""""
A multiline
string
"""
)
dedent(""""
A multiline
string
""")
```
Black intended to ship a similar style change as part of the 2024 style that always removes the indent. It turned out that this change was too disruptive to justify the cases where it improved formatting. Ruff introduced the new heuristic of preserving the indent. We believe it's a good compromise that improves formatting but minimizes disruption for users.
### Blank lines at the start of a block
Black 24 and newer allows blank lines at the start of a block, where Ruff always removes them:
```python
# Black
if x:
a = 123
# Ruff
if x:
a = 123
```
Currently, we are concerned that allowing blank lines at the start of a block leads [to unintentional blank lines when refactoring or moving code](https://github.com/astral-sh/ruff/issues/8893#issuecomment-1867259744). However, we will consider adopting Black's formatting at a later point with an improved heuristic. The style change is tracked in [#9745](https://github.com/astral-sh/ruff/issues/9745).
### Hex codes and Unicode sequences
Ruff normalizes hex codes and Unicode sequences in strings ([#9280](https://github.com/astral-sh/ruff/pull/9280)). Black intended to ship this change as part of the 2024 style but accidentally didn't.
```python
# Black
a = "\x1B"
b = "\u200B"
c = "\U0001F977"
d = "\N{CYRILLIC small LETTER BYELORUSSIAN-UKRAINIAN I}"
# Ruff
a = "\x1b"
b = "\u200b"
c = "\U0001f977"
d = "\N{CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I}"
```
### Module docstrings
Ruff formats module docstrings similar to class or function docstrings, whereas Black does not.
```python
# Input
"""Module docstring
"""
# Black
"""Module docstring
"""
# Ruff
"""Module docstring"""
```
### Walruses in slice expressions
@ -489,47 +645,6 @@ assert AAAAAAAAAAAAAAAAAAAAAA.bbbbbb.fooo(
) * foooooo * len(list(foo(bar(4, foo), foo)))
```
### Expressions with (non-pragma) trailing comments are split more often
Both Ruff and Black will break the following expression over multiple lines, since it then allows
the expression to fit within the configured line width:
```python
# Input
some_long_variable_name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
# Black
some_long_variable_name = (
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
)
# Ruff
some_long_variable_name = (
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
)
```
However, if the expression ends in a trailing comment, Black will avoid wrapping the expression
in some cases, while Ruff will wrap as long as it allows the expanded lines to fit within the line
length limit:
```python
# Input
some_long_variable_name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # a trailing comment
# Black
some_long_variable_name = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # a trailing comment
# Ruff
some_long_variable_name = (
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
) # a trailing comment
```
Doing so leads to fewer overlong lines while retaining the comment's intent. As pragma comments
(like `# noqa` and `# type: ignore`) are ignored when computing line width, this behavior only
applies to non-pragma comments.
### The last context manager in a `with` statement may be collapsed onto a single line
When using a `with` statement with multiple unparenthesized context managers, Ruff may collapse the
@ -563,7 +678,7 @@ with tempfile.TemporaryDirectory() as d1:
pass
```
In future versions of Ruff, and in Black's preview style, parentheses will be inserted around the
When targeting Python 3.9 or newer, parentheses will be inserted around the
context managers to allow for clearer breaks across multiple lines, as in:
```python