【小ネタ】Pythonのフォーマッター「black」で文字列の改行整形を無視する方法を考えてみた

blackによる文字列の改行整形を防ぐために試行錯誤してみました
2023.10.15

こんにちは、AWS事業本部@福岡オフィスのべこみん(@beco_minn)です。

今回は小ネタです。

自分も参加しているツール開発でPythonが使われているのですが、コードの書き方が統一されていないためリファクタリングしようという話になりました。

しかし、導入していた自動フォーマッターblackにより困った際の対処方法です。

あくまでも自己流の解決方法ですので、他に良い方法があればX(旧Twitter)等で教えて下さい。

最初にまとめ

  • 複数行にわたる文字列がある場合、改行を無視した合計文字数が88字以内の場合、blackでは改行が削除されて1行に整形される
  • 下記2通りの方法で自動整形されないことが分かった
    • 文字列の途中に何かしらのコメントを入れる
    • 改行を抜いた状態での文字数を89字以上にする
    • 公式のフォーマット無効化仕様を利用する
      • フォーマットを無効化したい行の末尾に # fmt: skip を追記する
      • フォーマットを無効化したい箇所を # fmt: off# fmt: on で囲む

背景

リファクタリング対象のコードを確認したところ、複数行の文字列が下記のように2通りの書き方をされていました。

str = "hoge"
str += "fuga"
str += "piyo"
str = (
    "hoge"
    "fuga"
    "piyo"
)

そこで可読性向上のためにも後者に寄せようとしたのですが、ファイルを保存するとblackにより下記のように自動整形されてしまいます。どうしよう。

str = "hoge" "fuga" "piyo"

解決方法

検証の結果、下記のようにコメントを入れると自動整形されないことが分かりました。

str = (
    # 無意味なコメント
    "hoge"
    "fuga"
    "piyo"
)

コメントの内容は何でも良いです。#だけでも良いです。ただし、挿入する場所は文字列が記載されている行以外である必要があります。

もしくは、自動整形により削除される改行を除いた除いた1行の文字数が 89字以上の場合 、自動整形は起こらないようです。

追記:公式に記載されているフォーマット無効化の方法

本記事執筆後に教えて頂いたのですが、blackの公式にフォーマット無効化の方法が記載してあったのでそちらも記載しておきます。

It doesn’t reformat lines that end with # fmt: skip or blocks that start with # fmt: off and end with # fmt: on. # fmt: on/off must be on the same level of indentation and in the same block, meaning no unindents beyond the initial indentation level between them.

blackのコードスタイルとして紹介されているフォーマット無効化の方法としては下記の2通りがあります。

  • フォーマットを無効化したい行の末尾に # fmt: skip を追記する
  • フォーマットを無効化したい箇所を # fmt: off# fmt: on で囲む
str = (
    "hoge"
    "fuga"
    "piyo"
) # fmt: skip

もしくは

# fmt: off
str = (
    "hoge"
    "fuga"
    "piyo"
)
# fmt: on

個人的には前者の方が好きですね。

ただし、この方法は恐らく「対象箇所のフォーマット適用を無効化する」という仕様のため、今回問題になっている88字制限以外のフォーマットルールも無効化されるかもしれません。上記ルールを利用する場合はその点を考慮の上、ご利用ください。

最後に

blackを使っている方であれば一度は遭遇したことのある問題ではないでしょうか。

コメント追加は少しハックっぽい解決方法ですが、自分は「# blackによる自動整形を防ぐためのコメント」のようなコメントを挿入しました。

本記事がどなたかのお役に立てれば幸いです。

以上、べこみんでした。