LoginSignup
3
0

More than 1 year has passed since last update.

Ruffによる`E501`を修正した後にblackでフォーマットすると、再度RuffによりE501が発生する

Posted at

環境

$ ruff --version
ruff 0.0.264

$ black --version
black, 23.3.0 (compiled: yes)
Python (CPython) 3.11.2

はじめに

Pythonのフォーマッターにblack、リンターにRuffを使用しています。
1行が長すぎる場合、RuffはE501(Line too long)エラーを出力します。E501を修正した後blackでフォーマットすると、再度RuffによりE501エラーが出力されるケースがありました。
その現象をまとめます。

なお、RuffのIssueで報告されている内容と同じ現象です。

設定ファイル

1行の最大長さを20文字とします。

pyproject.toml
[tool.ruff]
select = ["RUF100", "E501"]
line-length = 20

[tool.black]
line-length = 20

どのような現象か?

1.最初の状態

sample11.py
print("あい うえお")

1行目は15文字です。全角文字を2文字としてカウントすると、21文字です。

$ black sample11.py 
All done! ✨ 🍰 ✨
1 file left unchanged.

$ ruff check sample11.py 
sample11.py:1:15: E501 Line too long (21 > 20 characters)
Found 1 error.

blackは「1行目は20文字を超えていない」と判断して、何もしません。しかしRuffは全角文字を2文字としてカウントするため、「1行目は20文字を超えている」と判断して、E501エラーとします。

2.Ruffでnoqaコメントを付ける

E501エラーを無視するため、行末にnoqaディレクティブを付けます。

$ ruff check sample11.py --add-noqa
Added 1 noqa directive.
sample11.py
print("あい うえお")  # noqa: E501

1行目は29文字です。20文字を超えていて、かつ改行すれば20文字以下になるので、blackでフォーマット可能な状態になります。

$ black sample11.py --check
would reformat sample11.py

Oh no! 💥 💔 💥
1 file would be reformatted.

3.blackでフォーマット

$ black sample11.py 
reformatted sample11.py

All done! ✨ 🍰 ✨
1 file reformatted.
sample11.py
print(
    "あい うえお"
)  # noqa: E501

blackでフォーマットすると改行されて、すべての行が20文字以下になりました。

4.再度Ruffでチェック

再度Ruffでチェックすると、「# noqa: E501は不要」というエラーRUF100が発生しました。
--fixオプションを付けて、不要なnoqaディレクティブを取り除きます。

$ ruff check sample11.py
sample11.py:3:4: RUF100 [*] Unused `noqa` directive (unused: `E501`)
Found 1 error.
[*] 1 potentially fixable with the --fix option.

$ ruff check sample11.py --fix
Found 1 error (1 fixed, 0 remaining).
sample11.py
print(
    "あい うえお"
)

2行目は13文字です。全角文字を2文字とカウントすると、19文字なのでRuffのE501エラーは発生しません。

5.再度blackでフォーマット

$ black sample11.py 
reformatted sample11.py

All done! ✨ 🍰 ✨
1 file reformatted.
sample11.py
print("あい うえお")

blackでフォーマットすると1行になりました。改行を取り除いて1行にしても20文字以下になるためです。
そして、最初の状態に戻ってしまいました。

原因

以下の条件を全て満たしていると、「フォーマット→リンターによるチェック&修正→フォーマット→リンターによるチェック&修正→。。。」と、コードは常に変わってしまいます。

  • (a) フォーマットツールにblackを使用している
  • (b) リンターにRuffを使用している
  • (c) RuffはルールE501RU100をチェックする
  • (d) Ruffは1行の最大文字数を超えていると判断するが、blackは最大文字数を超えていないと判断する
  • (e) 行末に# noqa: E501を付けると、blackは最大文字数を超えていると判断する
  • (f) blackでフォーマットした後は、Ruffは1行の最大文字数を超えていないと判断する

ポイントは条件(d)です。Ruffが全角文字を1文字としてカウントしてくれれば、blackと"line-length"のカウント方法は同じになります。Ruffとblackの"line-length"の設定が一致しているならば、条件(d)が満たさせることはありません。
Flake8の"line-length"はblackと同じ(全角文字を1文字としてカウントする)なので、このようにコードが常に変化することはありませんでした。

なぜRuffは全角文字を1文字としてカウントしないのか?

Ruffのv0.0.260から、line-lengthの計算が"character count"から"Unicode width"に変更されました。

これは、blackの以下のPRに合わせたためです。以下のPRは、black v23.3.0でリリースされました。ただし"Unicode width"を使用するのはpreview版だけです。

以下は、blackの"line-length"の計算方法をpreview版と通常版で分岐している箇所です。
https://github.com/psf/black/blob/ef6e079901d53a42dfae4ab10b081ce7a73a47b5/src/black/lines.py#L763

解決策

blackのpreview版を利用する

最初の状態のPythonファイルに対して、--previewオプションを指定してblackを実行すると、以下の状態になります。

5$ black sample11.py --preview  
All done! ✨ 🍰 ✨
1 file left unchanged.
sample11.py
print(
    "あい うえお"
)

フォーマット後の状態では、RuffはE501エラーになりません。

Ruffのv0.0.260未満を利用する

Ruffのv0.0.260未満では、"line-width"の計算では全角文字を1文字としてカウントするので、blackの通常版と"line-width"の計算方法が同じになります。

RuffでルールE501またRU100をチェックしない

解決策というか回避策です。

"3.blackでフォーマット"後のときに、# noqa: E501,RUF100を付与する

print(
    "あい うえお"  # noqa: E501,RUF100
)
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0