Python でコードフォーマッタを使うメモ

適当に Python を書いているとコーディングスタイルが崩れがち（特に横幅が伸びまくる）ので、コードフォーマッタとコードチェッカーを導入することにしました。
このような記事は大量にあると思いますが自分の備忘録を兼ねて書いておきます。

## 環境

Windows 10 WSL 上で pyenv を使い Python 3.8.2 を動かしています。

## コードフォーマッタの選定

こちらの記事 https://www.kimoton.com/entry/20181223/1545540702 を参考に、まず autopep8 と yapf を試してみました。
どちらも pip で簡単にインストールできます：

`pip install autopep8`
`pip install yapf`

記事で試されているコード例とそのフォーマット結果を見ると、コンマ区切りの箇所で改行を毎回入れるかが違うようです。

特に長い配列で試してみると一目瞭然です：

```py:test.py
a = [1, 2, 3, 45, 6789, 998244353, 1000000007, 1000000009, 18446744073709551615, 1, 2, 3, 45, 6789, 998244353, 1000000007, 1000000009, 18446744073709551615, 123456789]
```

```text
$ autopep8 -a test.py
a = [
    1,
    2,
    3,
    45,
    6789,
    998244353,
    1000000007,
    1000000009,
    18446744073709551615,
    1,
    2,
    3,
    45,
    6789,
    998244353,
    1000000007,
    1000000009,
    18446744073709551615,
    123456789]
```

```text
$ yapf test.py
a = [
    1, 2, 3, 45, 6789, 998244353, 1000000007, 1000000009, 18446744073709551615,
    1, 2, 3, 45, 6789, 998244353, 1000000007, 1000000009, 18446744073709551615,
    123456789
]
```

（autopep8 の引数の `-a` は、`--aggressive` の短縮形です。これを指定しないと行の長さを短くするフォーマットを行ってくれないようです）

このように、autopep8 は要素ごとに改行を入れるのに対し、yapf は長さ制限を超過しない範囲で控えめに改行を入れるようです。
個人的な好みとして、要素ごとに改行を入れられると冗長になって読みづらいと思ったので、yapf を使うことにしました。

## yapf の基本的な使い方

`yapf <filename>` で、ファイル `<filename>` をフォーマットした結果が標準出力に出力されます。

yapf は細かくスタイルを制御できるようですが、ここではデフォルトの設定で使いたいと思います。
フォーマッタを使う上で基本的なオプションと思ったものは以下の通りです（`yapf --help` より抜粋）：

```text
  -d, --diff            print the diff for the fixed source
  -i, --in-place        make changes to files in place
  -r, --recursive       run recursively over directories
```

- `--diff` オプションをつけると、変更箇所のみを diff 形式で指摘してくれます。
- yapf は標準ではフォーマット後のファイル全体を標準出力に出力しますが、`--in-place` オプションをつけると、ファイルをフォーマット結果で上書きします。
- `--recursive` オプションをつけると、あるディレクトリ以下のファイルすべて（より下位のディレクトリ内部も含む）に対してフォーマットをかけることができます。

## コードチェッカー

コードチェッカーとしては flake8 を使うことにしました。
flake8 も pip でインストールできます。

`pip install flake8`

例えば先程のやたら横に長い `test.py` を flake8 にかけると「行が長すぎる」と言われますが、yapf をかけておくと解消することが確かめられます：

```text
$ flake8 test.py
test.py:1:80: E501 line too long (167 > 79 characters)
$ yapf -i test.py
$ flake8 test.py
$
```

なお flake8 は特に `-r` などのオプションをつけなくても、フォルダ以下全体に対してチェックをかけることが可能なようです。

## 部分的にコードチェック、フォーマットを無効にしたい場合

例えば 2 次元配列（リスト）などで、コンマの位置を行ごとで揃えたいということがあります：

```py:2darray.py
a = [
    [ 1, -2,  3],
    [-4,  5, -6],
    [ 7, -8,  9]
]
```

位置合わせのため、意図的に `[` の直後にスペースを入れるなどしていますが、これは flake8 にかけるとエラーとなります：

```text
$ flake8 2darray.py
2darray.py:2:6: E201 whitespace after '['
2darray.py:4:6: E201 whitespace after '['
```

[flake8 のドキュメント](https://flake8.pycqa.org/en/latest/user/violations.html#in-line-ignoring-errors) によると、行末に `# noqa: E201` というコメントを足しておくとエラーを無視することができます。
（こういうケースだと複数行にわたって同じエラーを無視させたいのですが、ドキュメントを見た限りだと、ファイル全体で例外に入れるでもしない限りそのようなことはできないようです…）

```py
a = [
    [ 1, -2,  3],  # noqa: E201
    [-4,  5, -6],  # noqa: E201
    [ 7, -8,  9]   # noqa: E201
]
```

```text
$ flake8 2darray.py
(何も表示されない)
```

が、この状態で yapf をかけると、相変わらず余計な空白とみなされて消されてしまいます：

```text
$ yapf 2darray.py
a = [
    [1, -2, 3],  # noqa: E201
    [-4, 5, -6],  # noqa: E201
    [7, -8, 9]  # noqa: E201
]
```

この場合、[yapf の ドキュメント](https://github.com/google/yapf/blob/master/README.rst) にあるようにコメントを追加すると、部分的にフォーマットを無効化することができます（flake8 の場合と違い、式単位とか範囲単位での指定もできるようです）：

```py
a = [
    [ 1, -2,  3],  # noqa: E201
    [-4,  5, -6],  # noqa: E201
    [ 7, -8,  9]   # noqa: E201
]  # yapf: disable
```

これで、無事 yapf のチェックも無効化できるようになりました。

```text
$ yapf -d 2darray.py
(何も表示されない)
```