Black
- navigation#
この文書では、Black と Ruff のフォーマッタ間のコードスタイルにおける既知の意図的な違いを列挙します。
意図しない逸脱のリストについては、issue trackerを参照してください。
Trailing end-of-line comments#
Black の優先事項は、たとえ行の終わりのコメントが含まれていても、ステートメント全体を 1 行に収めることです。
このような場合、Black は文を折りたたみ、折りたたまれた文の最後にコメントを移動します。:
# Input
while (
cond1 # almost always true
and cond2 # almost never true
):
print("Do something")
# Black
while cond1 and cond2: # almost always true # almost never true
print("Do something")
Ruff は、Prettierと同様に、末尾の行末コメントを含むステートメントを展開します。たとえば、Ruff は、上記のスニペットのwhileテストを折り畳むことを回避します。これにより、追加の垂直スペースを保持するという犠牲を払って、コメントが元の位置に近く、元の意図を保持することが保証されます。
Ruff の出力は、Black によってすでにフォーマットされているコードに対して逸脱すべきではないという点で、この逸脱はフォーマットされていないコードにのみ影響します。
Pragma comments are ignored when computing line width#
Pragma comments (# type, # noqa, # pyright, # pylint, etc.) are ignored when computing the width of a line.This prevents Ruff from moving pragma comments around, thereby modifying their meaning and behavior:
行の幅を計算するときに、プラグマコメント(#type、#noqa、#pyright、#pylint、など)は無視されます。これにより、Ruff がプラグマコメントを移動するのを防ぎ、その意味と動作を変更します。:
詳細については、Ruff のpragma comment handling proposalを参照。
これはPyinkに似ているが、Black. Black からの逸脱であります。Black は、#typeコメント(#997)を含む行の分割を回避するが、それ以外の場合は、特殊なケーシングのプラグマコメントを回避します。
Ruff は行の末尾のコメントを拡張するので、次のような場合にはプラグマコメントの移動も避けます。#noqaを行の末尾に移動すると、first()とsecond()の両方でエラーが抑制されます。:
# Input
[
first(), # noqa
second()
]
# Black
[first(), second()] # noqa
# Ruff
[
first(), # noqa
second(),
]
Line width vs. line length#
Ruff は、行の Unicode 幅を使用して、行が適合するかどうかを判断します。Black は、文字列に Unicode 幅を使用し、他のすべてのトークンに文字幅を使用します。Ruff は、識別子とコメントにも Unicode 幅を使用します。
Parenthesizing long nested-expressions#
Black 24 以降では、関数パラメータに長い条件式と型注釈が括弧で括られています。:
# 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 for an in-depth explanation of our concerns and an outline of possible alternatives.
Ruff のフォーマット(Black の 23 と一致する)は読みにくく、改善が必要であることに同意します。しかし、特に式のフォーマットを総合的に検討する場合には、長いネストされた式を括弧で括ることが最善の解決策であるとは確信していません。そのため、別のネストされた式のフォーマットスタイルを検討するまで決定を延期したいと考えています。懸念の詳細な説明と可能な代替案の概要については、psf/Black#4123を参照してください。
Call expressions with a single multiline string argument#
Black とは異なり、Ruff は呼び出し式で単一の複数行文字列引数のインデントを保持します。:
# 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 は、常にインデントを削除する 2024 スタイルの一部として、同様のスタイル変更を提供することを意図していました。この変更は、フォーマットを改善した場合を正当化するにはあまりにも破壊的であることがわかりました。Ruff は、インデントを維持するという新しいヒューリスティックを導入しました。これは、フォーマットを改善しながら、ユーザーの混乱を最小限に抑える優れた妥協案であると考えています。
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:
Black 24 以降では、ブロックの先頭に空白行を入れることができますが、Ruff では常に空白行を削除します。:
Currently, we are concerned that allowing blank lines at the start of a block leads to unintentional blank lines when refactoring or moving code. However, we will consider adopting Black's formatting at a later point with an improved heuristic. The style change is tracked in #9745.
現在、ブロックの先頭に空白行を許可すると、[to unintentional blank lines when refactoring or moving code]ことが懸念されています(https://github.com/astral-sh/ruf/issues/8893#issuecomment-1867259744)。ただし、改善されたヒューリスティックを使用して、後の時点でブラックのフォーマットを採用することを検討します。スタイルの変更は#9745で追跡されます。
Hex codes and Unicode sequences#
Ruff は、文字列内の 16 進コードとユニコードシーケンスを正規化します。(#9280)。ブラックはこの変更を 2024 スタイルの一部として出荷する予定でしたが、誤って出荷しませんでした。。
# 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 はモジュールの docstring をクラスや関数の docstring と同じようにフォーマットしますが、Black はそうではありません。
Walruses in slice expressions#
Black は、スライス内の:=演算子の周囲にスペースを挿入することを回避します。たとえば、次のものは Black の安定したスタイルに似ています::
Ruff は代わりに:=演算子の周りにスペースを追加します。:
これはおそらくブラックのプレビュースタイルに組み込まれるかもしれません。(#3823)。
global and nonlocal names are broken across multiple lines by continuations#
globalまたはnonlocal文に複数の名前が含まれていて、設定された線幅を超える場合、Ruff は継続を使用してそれらを複数の行に分割します。:
# Input
global analyze_featuremap_layer, analyze_featuremapcompression_layer, analyze_latencies_post, analyze_motions_layer, analyze_size_model
# Ruff
global \
analyze_featuremap_layer, \
analyze_featuremapcompression_layer, \
analyze_latencies_post, \
analyze_motions_layer, \
analyze_size_model
Newlines are inserted after all class docstrings#
通常、Black はクラスの docstring の後に単一の改行を強制します。ただし、docstring が三重引用符ではなく単一引用符で囲まれている場合は、そのような書式設定は適用されません。一方、Ruff は両方の場合に単一の改行を強制します。:
# Input
class IntFromGeom(GEOSFuncFactory):
"Argument is a geometry, return type is an integer."
argtypes = [GEOM_PTR]
restype = c_int
errcheck = staticmethod(check_minus_one)
# Black
class IntFromGeom(GEOSFuncFactory):
"Argument is a geometry, return type is an integer."
argtypes = [GEOM_PTR]
restype = c_int
errcheck = staticmethod(check_minus_one)
# Ruff
class IntFromGeom(GEOSFuncFactory):
"Argument is a geometry, return type is an integer."
argtypes = [GEOM_PTR]
restype = c_int
errcheck = staticmethod(check_minus_one)
Trailing own-line comments on imports are not moved to the next line#
Black は、インポートと末尾のワンラインコメントの間に単一の空行を強制します。Ruff は、そのようなコメントをそのまま残します:
# Input
import os
# comment
import sys
# Black
import os
# comment
import sys
# Ruff
import os
# comment
import sys
Parentheses around awaited collections are not preserved#
Black は、待機中のコレクションを囲む括弧を保持します。:
Ruff は代わりにそれらを削除します。:
これは、他の待機式のフォーマットとより一貫性があります。Ruff と Black は、例えばawait (1)のように括弧を削除し、例えばawait (x: =1 )のように構文的に必要な場合にのみ括弧を保持します。
Implicit string concatenations in attribute accesses#
次のフォーマットされていないコードがあるとします。:
内部的には、ブラックの論理は最初に最も外側のprint呼び出しを拡張します。:
引数がまだ長すぎるため、Black は最高の分割優先順位を持つ演算子で分割されます。この場合、Black は暗黙的な文字列連結を分割して、次の Black 形式のコードを生成します。:
Ruff は、行を分割するときに暗黙的な連結に"低い"優先順位を与えます。その結果、Ruff は代わりに上記を次のようにフォーマットします。:
一般に、Black は Ruff よりも暗黙的な文字列連結を複数行に分割することが多く、それらの連結が 1 行に収まる場合でも同様です。Ruff は、設定された線幅内に収まる必要がない限り、そのような連結の分割を回避します。
Own-line comments on expressions don't cause the expression to expand#
次のようなエクスプレッションがあるとします。:
Black はコメントをsome_example_varと関連付けて、コメントを 2 行に分けます。:
Ruff は代わりに、コメントをブール式全体に関連付けて、初期フォーマットを保持します。:
Tuples are parenthesized when expanded#
Ruff は(いくつかの例外を除いて)タプルを括弧で括る傾向があり、Black はタプル括弧をより頻繁に削除する傾向があります。
In particular, Ruff will always insert parentheses around tuples that expand over multiple lines:
例外が 1 つあります。forループでは、Ruff も Black も不必要な括弧の挿入を回避します。:
Single-element tuples are always parenthesized#
Ruff は常に単一要素のタプルの周りに括弧を挿入しますが、Black は場合によってはそれらを省略します。:
単一要素のタプルの周りに括弧を追加すると、視覚的な区別が追加され、余分な末尾のカンマによって作成される"偶発的な"タプルを回避するのに役立ちます(例えば、#17181を参照)。
Trailing commas are inserted when expanding a function definition with a single argument#
単一の引数を持つ関数定義が複数行に展開されると、引数に型注釈やデフォルト値が含まれているかどうかに応じて、Black は場合によっては末尾にカンマを追加します。
たとえば、Black 関数は、以下の 1 番目と 2 番目の関数定義に末尾のカンマを追加しますが、3 番目の関数定義には追加しません。:
def func(
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
) -> None:
...
def func(
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=1,
) -> None:
...
def func(
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: Argument(
"network_messages.pickle",
help="The path of the pickle file that will contain the network messages",
) = 1
) -> None:
...
Ruff は、一貫性を保つために、このようなすべての場合に末尾にカンマを挿入します。
Parentheses around call-chain assignment values are not preserved#
前提::
def update_emission_strength():
(
get_rgbw_emission_node_tree(self)
.nodes["Emission"]
.inputs["Strength"]
.default_value
) = (self.emission_strength * 2)
Black は(self.emission_strength * 2)の括弧を保持しますが、Ruff は括弧を削除します。
Black と Ruff はどちらも、次のような単純な割り当てでこのような括弧を削除します。:
# Input
def update_emission_strength():
value = (self.emission_strength * 2)
# Black
def update_emission_strength():
value = self.emission_strength * 2
# Ruff
def update_emission_strength():
value = self.emission_strength * 2
Call chain calls break differently in some cases#
Black は、Ruff とは異なる方法でコールチェーンを切断することがあります。特に、Black は、次のように、チェーン内の最後のコールの引数を拡張することがあります。:
# Input
df.drop(
columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
).drop_duplicates().rename(
columns={
"a": "a",
}
).to_csv(path / "aaaaaa.csv", index=False)
# Black
df.drop(
columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
).drop_duplicates().rename(
columns={
"a": "a",
}
).to_csv(
path / "aaaaaa.csv", index=False
)
# Ruff
df.drop(
columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
).drop_duplicates().rename(
columns={
"a": "a",
}
).to_csv(path / "aaaaaa.csv", index=False)
Ruff は、設定された線幅内に収まるようにする必要がある場合にのみ引数を展開します。
Black 関数では、この最終呼び出しの引数の区切りが例外なく適用されるわけではないことに注意してください。たとえば、Black 関数と Ruff 関数では、次の数式の書式が同じになります。:
# Input
df.drop(
columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]
).drop_duplicates(a).rename(
columns={
"a": "a",
}
).to_csv(
path / "aaaaaa.csv", index=False
).other(a)
# Black
df.drop(columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]).drop_duplicates(a).rename(
columns={
"a": "a",
}
).to_csv(path / "aaaaaa.csv", index=False).other(a)
# Ruff
df.drop(columns=["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]).drop_duplicates(a).rename(
columns={
"a": "a",
}
).to_csv(path / "aaaaaa.csv", index=False).other(a)
同様に、場合によっては、Ruff は、設定された線幅内に式を収めることができる場合、Black よりも積極的に合成バイナリ式を折り畳みます。:
# Black
assert AAAAAAAAAAAAAAAAAAAAAA.bbbbbb.fooo(
aaaaaaaaaaaa=aaaaaaaaaaaa
).ccccc() == (len(aaaaaaaaaa) + 1) * fooooooooooo * (
foooooo + 1
) * foooooo * len(
list(foo(bar(4, foo), foo))
)
# Ruff
assert AAAAAAAAAAAAAAAAAAAAAA.bbbbbb.fooo(
aaaaaaaaaaaa=aaaaaaaaaaaa
).ccccc() == (len(aaaaaaaaaa) + 1) * fooooooooooo * (
foooooo + 1
) * foooooo * len(list(foo(bar(4, foo), foo)))
The last context manager in a with statement may be collapsed onto a single line#
括弧で括られていない複数のコンテキスト・マネージャーを持つwith文を使用する場合、Ruff は最後のコンテキスト・マネージャーを 1 行にまとめることができます(そうすることでwith文が設定された行幅に収まるようになります)。
一方、Black は、次の例のように、最後のコンテキストマネージャをわずかに異なる方法で分割する傾向があります。:
# Black
with tempfile.TemporaryDirectory() as d1:
symlink_path = Path(d1).joinpath("testsymlink")
with tempfile.TemporaryDirectory(dir=d1) as d2, tempfile.TemporaryDirectory(
dir=d1
) as d4, tempfile.TemporaryDirectory(dir=d2) as d3, tempfile.NamedTemporaryFile(
dir=d4
) as source_file, tempfile.NamedTemporaryFile(
dir=d3
) as lock_file:
pass
# Ruff
with tempfile.TemporaryDirectory() as d1:
symlink_path = Path(d1).joinpath("testsymlink")
with tempfile.TemporaryDirectory(dir=d1) as d2, tempfile.TemporaryDirectory(
dir=d1
) as d4, tempfile.TemporaryDirectory(dir=d2) as d3, tempfile.NamedTemporaryFile(
dir=d4
) as source_file, tempfile.NamedTemporaryFile(dir=d3) as lock_file:
pass
Python 3.9 以降を対象とする場合、複数行にまたがるブレークをより明確にするために、次のようにコンテキスト・マネージャーの周囲に括弧が挿入されます。:
with tempfile.TemporaryDirectory() as d1:
symlink_path = Path(d1).joinpath("testsymlink")
with (
tempfile.TemporaryDirectory(dir=d1) as d2,
tempfile.TemporaryDirectory(dir=d1) as d4,
tempfile.TemporaryDirectory(dir=d2) as d3,
tempfile.NamedTemporaryFile(dir=d4) as source_file,
tempfile.NamedTemporaryFile(dir=d3) as lock_file,
):
pass