【black】Pythonのソースコードを自動整形!!コードフォーマットで議論するのはもう止めませんか?
Pythonのコードを自動整形するフォーマッター、blackを紹介します。
コードフォーマットをフォーマッターに任せることで、
フォーマットではなくロジックなどに議論を集中することができます。
コードフォーマットで議論するのはもう止めませんか?
blackの特徴
最大の特徴は「設定がほとんどできない」
Pythonのフォーマッターとしては他にも、
- autopep8
- yapf
などありますが、
blackの最大の特徴はなんと言っても、設定がほとんどできないことです。
「え?それじゃあ使い勝手最悪じゃん」
と思ってしまうのも無理はありません。
僕もそう思ってましたから。
「設定がほとんどできない」の利点
設定に関して議論することや悩むことがなくなること
コードのフォーマットには正解がなく、人によって好みがかなり分かれます。
チームで開発する場合これは仕方ない問題であり、
だからこそフォーマッターを導入してコードのフォーマットを統一するのです。
「設定がほとんどできない」ことによって、blackに全てを任せることができます。
blackを半年程度使ってきましたが、「このフォーマットの仕方はいけてないな」と思ったことはほとんどありません。
導入当初、同僚が
「 ' が " にフォーマットされるのだけは許せない」
と言っていましたが、
「blackがそう決めているので仕方ないですね〜」
と議論になるまでもありませんでした。blackに責任転嫁することができるからです。
しばらく使えばblackのフォーマットに慣れますよ、きっと。
blackで設定できること
じゃあ逆に何なら設定できるのか。
pyproject.toml
に以下の設定を加えることができます。(詳細は後述)
- 1行の最大の文字数
- 対象のPythonバージョン
- 対象ファイル
- 対象外ファイル
VSCodeで使う場合
VSCodeで使う場合、
- 1行の最大の文字数
の設定のみが可能です。(詳細は後述)
導入方法
次に、blackの導入方法を紹介していきます。
インストール
$ pip install black
設定ファイルを作成
プロジェクトのルートディレクトリ(一番上位の階層)に pyproject.toml
を作成します。
[tool.black] line-length = 88 target-version = ['py37'] include = '\.pyi?$' exclude = ''' ( /( \.eggs # exclude a few common directories in the | \.git # root of the project | \.hg | \.mypy_cache | \.tox | \.venv | _build | buck-out | build | dist )/ | foo.py # also separately exclude a file named foo.py in # the root of the project ) '''
1行の最大文字数
line-length = 88
88文字を超えると改行されます。
flake8を導入している場合、設定を合わせると良いでしょう。
Pythonバージョン
target-version = ['py37']
指定したバージョンに対応できる形にフォーマットされます。
対応バージョンは以下。
[py27|py33|py34|py35|py36|py37|py38]
対象ファイル
include = '\.pyi?$'
該当するファイルをフォーマットします。
正規表現で書かれているのでわかりづらいですが、.py
.pyi
のどちらかのファイルを対象にフォーマットを実行します。
\
は.
をエスケープする?
は直前の文字が0, 1回出現することを示す$
は末尾
.pyi
はJupyterで作成したファイルに付けられる拡張子です。Jupyterを使わない場合、
include = '\.py$'
で良いと思います。
対象外ファイル
exclude = ''' ( /( \.eggs # exclude a few common directories in the | \.git # root of the project | \.hg | \.mypy_cache | \.tox | \.venv | _build | buck-out | build | dist )/ | foo.py # also separately exclude a file named foo.py in # the root of the project ) '''
該当するファイルはフォーマットしません。
blackをコマンド実行する
$ black .
カレントディレクトリ(今いるディレクトリ)から下の階層をまとめてフォーマットします。
black
コマンドで使える、主なオプションも紹介していきます。
[--check
オプション] ルールに従っているかのチェック
blackのルールに従っているかのチェックをする(フォーマットはされません)
$ black --check .
問題なければ↓
All done! ✨ 🍰 ✨ 42 files would be left unchanged.
フォーマット対象があると↓
would reformat /<省略>/black_test.py Oh no! 💥 💔 💥 1 file would be reformatted, 42 files would be left unchanged.
エラーが発生すると↓()
の閉じが足りなかったため、エラーが発生していました)
error: cannot format /<省略>/black_error.py: Cannot parse: 25:25: if __name__ == "__main__": Oh no! 💥 💔 💥 41 files would be left unchanged, 1 file would fail to reformat.
[--diff
オプション] フォーマットの差分を確認する
フォーマットの差分を確認できます。(これもフォーマットはされません)
$ black --diff .
Githubとかでおなじみに形式で出力されます。-
が変更前 +
が変更後を示しています。(以下は改行していたのが1行に直されてます)
--- black_test.py 2021-01-17 09:24:56.196098 +0000 +++ black_test.py 2021-01-17 09:25:00.376769 +0000 @@ -7,13 +7,11 @@ def run(): n = int(input()) - tree = [ - Node() for _ in range(n) - ] + tree = [Node() for _ in range(n)] @@ -26,6 +24,5 @@ - would reformat black_test.py All done! ✨ 🍰 ✨ 1 file would be reformatted, 42 files would be left unchanged.
VSCodeでファイル保存時に自動でフォーマットさせる
black
コマンドの使い方を紹介しましたが、あれでは毎回コマンドを打ち込んで実行する必要があります。
VSCodeを使っているのであれば、ファイル保存時に自動でフォーマットさせるのがオススメです。
VSCodeの左下の歯車マークから設定を開くか、settings.json
に以下の設定を追加します。
{ "python.formatting.provider": "black", "python.formatting.blackArgs": ["--line-length", "88"], "editor.formatOnSave": true }
VSCodeでは最大文字数のみ設定できるようです。
チームで開発している場合、.vscode/settings.json
を作成してgit管理することを強くオススメします!
人によって設定が違うと、保存した人が変わる度に毎回同じ箇所がフォーマットされてしまうことがあります...(実体験)
pre-commitでコミット前に毎回チェックさせる
VSCode以外にも、pre-commitを使う選択肢もあります。
1.pre-commitをインストール
$ pip install pre-commit
2.設定ファイル .pre-commit-config.yaml
を作成
repos: - repo: https://github.com/ambv/black rev: 19.10b0 hooks: - id: black types: [python] language_version: python3.8
3.pre-commitを設定
$ pre-commit install
まとめ
「設定がほとんどできない」が強みのblackを紹介しました。
- コードのフォーマットに関して議論しなくて済む
- 設定で議論しなくて済む
- VSCodeやpre-commitでフォーマットを自動化できる