Python の Lint (文法チェッカ) まとめ - flake8 + hacking を使う -

# 結論を先に

これから紹介する中で hacking が一番厳しい条件でチェックしてくれる。 hacking を使おう。私は使っている。
さらに禁欲的にするために、 flake8-import-order と flake8-pep257 も使おう。

```shell-session:install
$ pip install hacking flake8-import-order flake8-pep257
```
```shell-session:使い方
$ flake8 [対象]
```

直すのが大変そうな時も ```pyformat``` や ```isort``` など補助してくれそうなツールがある。

2015-08-11 追記 (既に修正済み): 
PEP 257 周りの説明で事実の誤認があった。 hacking だけでは PEP 257 のチェックはしないようだ。
flake8-pep257 をインストールすることでチェックできる。

## エディタの補助

可能な限りはやく気がつくため、エディタのプラグイン等で自動チェックできるなら、した方がいい。
以下は代表的なエディタでの flake8 でチェックするプラグイン。
これらの機能を取り込んだ IDE 化するプラグインもあるので、好きな方を使おう。

- Vim ... [vim-flake8](https://github.com/nvie/vim-flake8)
- Emacs ... [flymakes-python-pyflakes](https://github.com/purcell/flymake-python-pyflakes)
- Sublime Text ... [Python Flake8 Lint](https://github.com/dreadatour/Flake8Lint)
- Atom ... [linter-flake8](https://atom.io/packages/linter-flake8)

↑ のうち、私は Vim と Sublime Text については自分で検証して使えることを確認した。
Emacs, Atom については試してみたものの、勝手がわからず動作が確認できなかった。頑張って設定して欲しい。

### pyenv と共存する場合

.pyenv/shims/flake8 をエディタが触れるように、 PATH を工夫する必要があるかもしれない。

# はじめに

Python は動的な型付けを行う言語だけれど、禁欲的な文法とオフサイドルールのおかげで、実行以前に問題を発見できることが多々ある。
また、より細かいルールを制定する文化とチェックするツールがとても充実している。 (例: PEP 8 と pep8 )

人間がおのおのでルールを定めてミスを指摘しあうよりも、統一的なルールを作り、機械的に検証させる方が合理的だ。
今回は、自分の知っている Python の Lint ツールを列挙してみる。

# サンプル

以下のような、わざと少しずつ余分な要素を含んだ Python ファイルを用意した。
不自然な空行やスペース、余分な変数と import を入れた。

実行内容はフィボナッチ数列の最初の 10 個の数字を取り出すもの。

```py3:main.py


import sys  # 余分な import
import os
def fib():
    x = 0  # 使ってない変数
    a, b = 1, 1


    while True:
        yield    a    # 余分なスペース
        a, b = b, a + b


if __name__ == '__main__':
    for i, num in enumerate(fib()):
        print(num)
        if i >= 9:
            break
```

# pep8

PEP 8 https://www.python.org/dev/peps/pep-0008/ 

のスタイルに従っているかチェックすることができる

```shell-session:pep8
$ pep8 main.py
main.py:4:1: E302 expected 2 blank lines, found 0
main.py:9:5: E303 too many blank lines (2)
main.py:10:14: E271 multiple spaces after keyword
```

改行やスペース幅など指摘してくれる。

# pep257

docstring が

PEP 257 https://www.python.org/dev/peps/pep-0257/

のスタイルに従っているかチェックしてくれる

# pyflakes

https://github.com/pyflakes/pyflakes/

未使用の変数や未宣言の変数の使用、不要なインポートのチェックなどを行うことができる。

```shell-session:pyflakes
$ pyflakes main.py
main.py:2: 'sys' imported but unused
main.py:3: 'os' imported but unused
main.py:5: local variable 'x' is assigned to but never used
```

# flake8

flake8 は pep8 と pyflakes の両方の内容をチェックする。
番号 FXXX が pyflakes, EXXX が pep8 で扱うものと対応する。

```shell-session:flake8
$ flake8 main.py
main.py:2:1: F401 'sys' imported but unused
main.py:3:1: F401 'os' imported but unused
main.py:4:1: E302 expected 2 blank lines, found 0
main.py:5:5: F841 local variable 'x' is assigned to but never used
main.py:9:5: E303 too many blank lines (2)
main.py:10:14: E271 multiple spaces after keyword
```

# hacking

flake8 に加えて OpenStack Style Guidelines のルールをチェックしてくれる。
http://docs.openstack.org/developer/hacking/

HXXX の番号になっている。

```shell-session:hacking
$ flake8 main.py
main.py:2:1: F401 'sys' imported but unused
main.py:3:1: F401 'os' imported but unused
main.py:3:1: H306  imports not in alphabetical order (sys, os)
main.py:4:1: E302 expected 2 blank lines, found 0
main.py:5:5: F841 local variable 'x' is assigned to but never used
main.py:9:5: E303 too many blank lines (2)
main.py:10:14: E271 multiple spaces after keyword
```

```
main.py:3:1: H306  imports not in alphabetical order (sys, os)
```

サンプルの「 import がアルファベット順に並んでない！」という細かい点までちゃんと指摘してくれる。
ありがたい。

他にも Git リポジトリが含まれていると、コミットメッセージの書式まで言及してくれる。

# flake8-pep257

flake8 のプラグイン。 flake8 で PEP 257 をチェックすることができる。
https://pypi.python.org/pypi/flake8-pep257/1.0.2
flake8-docstrings というパッケージもあり、ほとんど同じ動作をするらしい。
2015 年 8 月にチェックした様子だと、最近までメンテナンスされているのは flake8-pep257 の方のようだ。

DXXX という番号は flake8-pep257 によるもの。

```
$ pip install flake8-pep257
```

# import-order

import の順序をチェックしてくれる

- __future__
- 標準ライブラリ
- site-packages
- ローカルファイル

というふうに import をグループにして、
グループごとにそれぞれ区別して 1 行空け、
アルファベット順に並ぶのが正しいとする。

https://pypi.python.org/pypi/import-order

# flake8-import-order

flake8 で上記の import の順序もチェックしてくれるプラグイン。

番号は IXXX 

# まとめ

あえて守備範囲を不等号であらわすとこんな関係

```
flake8 ⊃ pep8
flake8 ⊃ pyflakes
flake8 + flake8-pep257 ⊃ pep257
flake8 + flake8-import-order ⊃ import-order
```

もっと細かい使い方は ```-h``` オプションで出るヘルプを読もう

# まだまだあるぞ flake8 プラグイン

```
$ pip search flake8
```

で検索してみよう。
自分では試していないけど興味深いものとして

- flake8-coding ... Magic Comment ```# -*- coding: utf-8 -*-``` のような行をチェック
- flake8-double-quotes (flake8-quotes) ... 文字列を ```"``` で囲むようスタイルかチェック
- flake8-print ... print 使ってないかチェック（ print デバッグするな！）
- flake8-todo ... TODO 行の書き方チェック
- pep8-naming ... 命名規則のチェック
- flake8-copyright ... 著作権表示のチェック

などがある。
可能な限り禁欲的なコーディングを行うために、それぞれ試してみる価値があると思う。


# mccabe と radon

コードの循環的複雑度をチェックするツール。
hacking をインストールすると mccabe も入ってくる。
単体では radon の方が見やすい結果を表示してくれる。
普段から max-complexity を可能な限り小さい値で書いていくと、禁欲的で短い関数が書けるようになっていく。

# オマケ

## autopep8

PEP 8 に対応するように自動で変換することができる
https://github.com/hhatto/autopep8

```shell-session
$ autopep8 -i main.py
```

```py3:main.py(autopep8後)

import sys
import os


def fib():
    x = 0  # 使ってない変数
    a, b = 1, 1

    while True:
        yield a    # 余分なスペース
        a, b = b, a + b


if __name__ == '__main__':
    for i, num in enumerate(fib()):
        print(num)
        if i >= 9:
            break
```

## autoflake

同様に autoflake という、自動で pyflakes に対応するコマンドがある
https://github.com/myint/autoflake

```shell-session:autofalke
$ autoflake -i --remove-all-unused-imports --remove-unused-variables main.py
```

でも、変数を消すのはあまり得意ではなく、 上の例だと

```shell-session
main.py:3: local variable 'x' is assigned to but never used
```

は残ってしまう。万能薬や特効薬ではないけど、覚えておいて損はない。

## isort

さらに、 import の順序も自動で並び替え直す方法がある
https://github.com/timothycrosley/isort

```shell-session:isort
$ isort -rc main.py 
```

## docformatter

PEP 257 になるべく準拠させするように変換する。

## pyformat

autopep8 と autoflake と docformatter を足したもの。
isort は含んでいない。

## インストール方法

pip で

```
$ pip install pyformat
$ pip install isort
```

で OK 。詳細はやはり ```-h``` や ```--help``` オプションで。


# おすすめの使い方

tox.ini に記述して Jenkins や他 CI ツールと組み合わせて、テストの際に自動でツッコミが入るようにする。
あるいは、ツッコミの内容に対して pyformat をかけてしまえば、
警告するだけでなく自動で修正内容をプルリクエストしてくれる慈悲深い Bot も作れると思う。