Pythonの静的解析ツールFalke8を使ってみよう
Flake8について
Flake8 は PyFlakes,、pycodestyle、 McCabe のラッパーで、Pythonの静的解析に最もよく使われているツールです。mccabeは循環的複雑度というコードの複雑度をチェックできるツールですが、flake8のデフォルトでは無効になっています。
pycodestyle について
pycodestyle:以前の名前はpep8 だったもので、PythonのコードがPEP8に準拠しているかをチェックするためのリンターです。
pycodestyle は flake8 をはじめ、様々な静的解析ツールでも利用されているため、単独でインストールして使用することは少ないはずです。
pyflakesについて
Pyflakes はPython のソースファイルのエラーをチェックするシンプルなプログラムです。Pyflakes はプログラムを解析し、様々なエラーを検出します。ソースファイルをインポートするのではなく、解析することで動作するので、 副作用のあるモジュールでも安心して使えます。また、pylint よりもはるかに高速です。
code: bash
$ pip install pyflakes
Pyflakes が Pylintよりも高速な理由は、Pyflakesが各ファイルのシンタックスツリーを個別に調べているだけだからです。結果として、Pyflakesはチェックできる項目がより限定されています。
実際、pylint のサンプルで示した mandelbrot.py を pyflakes にかけてみると何も出力されません。つまり、何も問題がないということです。
code: bash
$ pyflakes mandelbrot.py
$
これだけでは、使い勝手がよくないように思われるかもしれませんが、このツールが開発された背景を考えれば納得がいきます。開発当初、pyflakes の開発者は「インターンが一人もいない、長年の経験を積んだ開発者ばかり」という環境で仕事をしていました。同僚達のードの品質は「非常に高い」ため、彼のツールは小さな問題をキャッチするだけでよかったわけです。
Pyflakesはスタイルチェックを行いませんが、PEP8に対するスタイルチェックとPyflakesを組み合わせたFlake8というツールもあります。Flake8 はスタイルチェックに加えて、プロジェクトごとの強力な設定機能も追加されています。
オプションも--helpと--versionしかなく、カスタマイズを行う仕組みが提供されていませんが、そのことは学習コストを低くすることにつながっています。
設計思想のひとつに「偽陽性を出さない」があり、そのための並々ならぬ努力をしているため、「エラーのないコード」を作成するための、必携ツールとなります。
pyflakees は flake8 をはじめ、様々な静的解析ツールでも利用されているため、単独でインストールして使用することは少ないはずです。
Flake8のプラグイン
Flake8が素晴らしいのは、非常に多くのプラグインが存在するからです。例えば名前に "flake8 "という文字列が含まれるパッケージが223は存在しています。(この資料作成時)
ほんとうに多数あるなかから、次のプラグインを紹介します。といっても、たくあんありすぎるためその存在だけになります。
cohesion: クラスのまとまりがしきい値を下回っているかどうかをチェックします。これは、機能をクラスから切り離すべきであることを示しています。.
flake8-assert-msg: assert 文にメッセージがあることを確認します。
flake8-blind-except: 例外をブラインドキャッチしている except文があるかを調べます。
flake8-builtins: 変数やパラメータとして使用されているpython組み込み関数をチェックします。
flake8-docstrings: pydocstyleのサポートを追加します。
flake8-isort: isort を使ってimportが期待通りにソートされているかどうかを確認します。
flake8-logging-format: ロギングフォーマット文字列の不足を検証します。
flake8-pytest-style: pytest ベースのテストでよくあるスタイルの問題や不整合のチェックします。
flake8-requirements: パッケージのインポート要件をチェック/検証します。
flake8-graphql: GraphQLのクエリ文字列を解析しチェックします。
flake8_implicit_str_concat: Python 暗黙のうちに同一行に連結された文字列リテラル(コード整形ツール Black によってもたらされる可能性があります)や、明示的な文字列リテラル連結のための不要なプラス演算子などのスタイル問題を探します。
flake8-mock: 誤った使い方をしているモック(mock:テストなどでの代替コード)をチェックします。
flake8-nb: Jpyter Notebook をチェックします。
flake8-pyi: スタブファイル(stub file:型の情報だけを書いたファイル(PEP 484))をチェックします。
flake8-variables-names: よくある「意味のない」変数名をします。
pep8-naming: PEP 8の命名規則に沿ってコードをチェックします。
pandas-vet: Pandasコードのための(押し付けた)意見付きでチェックします。
wemake-python-styleguide: かなり人気のある意見型のスタイルガイド/チェッカー。
flake8-bandit bandit を使ってセキュリティテストを行います。
flake8-bugbear: プログラムにありそうなバグや設計上の問題点を見つけます。
flake8-requests: requestsームワークの使用状況をチェックします。
flake8-breakpoint は忘れられたブレークポイントをチェックします。
flake8-print:すべての print 文に文句を言います。
flake8-debugger: pdb;idbp のインポートとトレースの設定、および IPython.terminal.embed のインポート InteractiveShellEmbed と InteractiveShellEmbed()() をチェックします。
flake8-fixme: FIXME、TODO、その他の一時的な開発者ノートをチェックします。
flake8-eradicate: コメントアウトされた(いわゆる「死んだ」)コードを見つけます。
lake8-comprehensions: より良いリスト/セット/辞書の作成を支援します 。
flake8-executable: 実行可能なファイルのパーミッションとシークエンスをチェックします。
flake8-raise: raiseステートメントの改善点を見つけます。
flake8-pytest: assertEqual の代わりに assert を使用することができます。
flake8-use-pathlib: pathlibモジュールで置き換え可能な関数の使用を見つけます。
flake8-sfs:Pythonの文字列フォーマットの優先順位を強制します。
flake-string-format:str.format を使って文字列やパラメータをチェックします。
flake8-printf-formatting: printfスタイルの文字列フォーマットを禁止します。
flake8-colors: Flake8のANSIカラーハイライト
flake8-csv エラーレポートをCSV形式で生成
flake8-json: JSON フォーマットでエラーレポートを生成
flake8-dashboardとflake8-html: HTMLレポートの生成(ダッシュボードのデモ
flake8-immediate: エラーを遅延なく直接印刷します
flake8-strftime: プラットフォーム固有の strftime コードの使用をチェックします。
flake8-SQL: SQL クエリを探し、意見のあるスタイルに照らし合わせてチェックします
flake8-tuple: (おそらく)意図しない1要素のタプルをチェックします。
flake8 を使ってみる
インストールは pip コマンドで行います。必要に応じて  pyflake8 と pycodestyle および mccabe も合わせてインストールされます。
code: bash
$ pip install flake8
簡単な使用方法は、 flake8 につづけて ファイルパスかディレクトリパスを与えるだけです。引数を省略するとカレントディレクトリのすべてのファイルがチェックされます。
code: bash
$ flake8 mandelbrot.py
mandelbrot.py:3:1: E302 expected 2 blank lines, found 1
mandelbrot.py:11:1: E302 expected 2 blank lines, found 1
mandelbrot.py:19:52: E202 whitespace before ')'
--helpオプションを与えて実行すると簡単なヘルプメッセージが出力されます。
code: bash
% flake8 --help
usage: flake8 options file file ...
positional arguments:
filename
optional arguments:
-h, --help show this help message and exit
-v, --verbose Print more information about what is happening in
flake8. This option is repeatable and will increase
verbosity each time it is repeated.
--output-file OUTPUT_FILE
Redirect report to a file.
--append-config APPEND_CONFIG
Provide extra config files to parse in addition to the
files found by Flake8 by default. These files are the
last ones read and so they take the highest precedence
when multiple files provide the same option.
--config CONFIG Path to the config file that will be the authoritative
config source. This will cause Flake8 to ignore all
other configuration files.
--isolated Ignore all configuration files.
--version show program's version number and exit
-q, --quiet Report only file names, or nothing. This option is
repeatable.
--count Print total number of errors and warnings to standard
error and set the exit code to 1 if total is not
empty.
--diff Report changes only within line number ranges in the
unified diff provided on standard in by the user.
--exclude patterns Comma-separated list of files or directories to
exclude. (Default: ['.svn', 'CVS', '.bzr', '.hg',
'.git', '__pycache__', '.tox', '.eggs', '*.egg'])
--extend-exclude patterns
Comma-separated list of files or directories to add to
the list of excluded ones.
--filename patterns Only check for filenames matching the patterns in this
comma-separated list. (Default: '*.py')
--stdin-display-name STDIN_DISPLAY_NAME
The name used when reporting errors from code passed
via stdin. This is useful for editors piping the file
contents to flake8. (Default: stdin)
--format format Format errors according to the chosen formatter.
--hang-closing Hang closing bracket instead of matching indentation
of opening bracket's line.
--ignore errors Comma-separated list of errors and warnings to ignore
(or skip). For example, --ignore=E4,E51,W234.
(Default: ['E123', 'E24', 'W503', 'E226', 'E121',
'E126', 'E704', 'W504'])
--extend-ignore errors
Comma-separated list of errors and warnings to add to
the list of ignored ones. For example, --extend-
ignore=E4,E51,W234.
--per-file-ignores PER_FILE_IGNORES
A pairing of filenames and violation codes that
defines which violations to ignore in a particular
file. The filenames can be specified in a manner
similar to the --exclude option and the violations
work similarly to the --ignore and --select
options.
--max-line-length n Maximum allowed line length for the entirety of this
run. (Default: 79)
--max-doc-length n Maximum allowed doc line length for the entirety of
this run. (Default: None)
--indent-size n Number of spaces used for indentation (Default: 4)
--select errors Comma-separated list of errors and warnings to enable.
For example, --select=E4,E51,W234. (Default: ['E',
'F', 'W', 'C90'])
--extend-select errors
Comma-separated list of errors and warnings to add to
the list of selected ones. For example, --extend-
select=E4,E51,W234.
--disable-noqa Disable the effect of "# noqa". This will report
errors on lines with "# noqa" at the end.
--show-source Show the source generate each error or warning.
--no-show-source Negate --show-source
--statistics Count errors and warnings.
--enable-extensions ENABLE_EXTENSIONS
Enable plugins and extensions that are otherwise
disabled by default
--exit-zero Exit with status code "0" even if there are errors.
-j JOBS, --jobs JOBS Number of subprocesses to use to run checks in
parallel. This is ignored on Windows. The default,
"auto", will auto-detect the number of processors
available to use. (Default: auto)
--tee Write to stdout and output-file.
--benchmark Print benchmark information about this run of Flake8
--bug-report Print information necessary when preparing a bug
report
mccabe:
--max-complexity MAX_COMPLEXITY
McCabe complexity threshold
pyflakes:
--builtins BUILTINS define more built-ins, comma separated
--doctests also check syntax of the doctests
--include-in-doctest INCLUDE_IN_DOCTEST
Run doctests only on these files
--exclude-from-doctest EXCLUDE_FROM_DOCTEST
Skip these files when running doctests
Installed plugins: mccabe: 0.6.1, pycodestyle:2.8.0, pyflakes: 2.4.0
デフォルトの出力フォーマットは次の通りです。
ファイル名;行数:カラム数:エラーコード:メッセージ
ほとんどのメッセージは説明不要ですが、エラーコードは次のように対応しています。
E***: pep8のエラーコード
W***: pep8の警告
F***: プラグイPyFlakesのエラーコード
C9**:プラグイン mccabe からのコード
N8**: プラグイン pep8-namingのエラーコード
書くエラー
PEP8 エラーコード
PEP8命名規則エラーコード
pyflakes エラーコード
設定ファイル
setup.cfgファイルを使って、いくつかのプロジェクトベースの設定パラメータを渡すことができます。
code: setup.cfg
flake8
exclude = .git,*migrations*
max-line-length = 119
この設定では、excludeパラメータでは、ファイル/ディレクトリを無視するために使用します。Python コーディングスタイルガイド(PEP8)に従えば、行の最大長は79です。これでは短いと思うときもあります。例えば、Django のコードスタイルガイドラインに従った場合は、行の最大長119 になります。そこで、max-line-lengthパラメータで変更しています。
ソースコード中でエラー無視
ソースコードに# noqa あるいは# noqa エラーコードを記述すると、その行について該当するチェックを無視します。
code: mondelbrot_noqa.py
import numpy as np
def mandelbrot(z: complex, max_iter: int):
c = z
for n in range(max_iter):
if abs(z) > 2:
return n
z = z*z + c
return max_iter
def mandelbrot_set(xmin: float, xmax: float,
ymin: float, ymax: float,
width: int, height: int,
max_iter: int):
horizon = np.linspace(xmin, xmax, width)
vertical = np.linspace(ymin, ymax, height)
return (horizon, vertical,
[mandelbrot(complex(r, i), max_iter)
for r in horizon for i in vertical] ) # noqa: E202
ソースコード全体でエラー無視
ソースコードに# falke8: noqa が記述されていると、そのファイル全体が無視されます。
ただし、Flake8 はこの方法よりも、無視したいファイルを --exclude オプショにより、明示的に除外リストに追加することを推奨しています。
flake8 のプラグイン
よくある無意味な変数(i, j, kなど)を検出するプラグイン flake8-variables-names を追加してみます。
code: bash
$ pip install flake8-variables-names
もう一度、flake8 を実行してみると、メッセージが増えています。
code: bash
% flake8 mandelbrot.py
mandelbrot.py:3:1: E302 expected 2 blank lines, found 1
mandelbrot.py:3:16: VNE001 single letter variable names like 'z' are not allowed
mandelbrot.py:4:5: VNE001 single letter variable names like 'c' are not allowed
mandelbrot.py:5:9: VNE001 single letter variable names like 'n' are not allowed
mandelbrot.py:8:9: VNE001 single letter variable names like 'z' are not allowed
mandelbrot.py:11:1: E302 expected 2 blank lines, found 1
mandelbrot.py:19:52: E202 whitespace before ')'
wemake-python-styleguide
Flake8は柔軟で使いやすく高速にPython スクリプトをチェックしてくれます。ただ、あまりにも豊富なプラグインがあるためどれをインストールすればよいのか迷うこともあります。なにより複数のプラグインをインストールすることは面倒なことです。
そうした場合は、wemake-python-styleguide を使ってみることをおすすめします。このパッケージは実のところ、Flake8のプラグインで、複数のプラグインを依存パッケージとして一度にインストールすることができるので便利です。
次のコマンドで、flake8 を含めて必要なパッケージをインストールができます。
code: bash
$ pip install wemake-python-styleguide
code: bash
% flake8 --help | sed -ne '/^Installed plugins/,$p'
Installed plugins: flake8-bandit: 2.1.2, flake8-broken-line: 0.3.0,
flake8-bugbear: 21.9.2, flake8-comprehensions: 3.7.0, flake8-darglint: 1.8.1,
flake8-debugger: 4.0.0, flake8-docstrings: 1.6.0, pydocstyle: 6.1.1,
flake8-eradicate: 1.2.0, flake8-string-format: 0.3.0, flake8-variables-names:
0.0.4, flake8_commas: 2.1.0, flake8_isort: 4.1.1, flake8_quotes: 3.3.1,
mccabe: 0.6.1, naming: 0.11.1, pycodestyle: 2.7.0, pyflakes: 2.3.1, rst-
docstrings: 0.2.3, wemake_python_styleguide: 0.15.3
この資料作成時点では、wemake-python-styleguide のバージョンは 0.15.3 で、これをインストールすると Flake-3.9.2 がインストールされます。依存関係はコード上の問題ではないので、つぎのようにして Flake8 を最新にすることができます。
code: bash
$ pip install -U --no-dependencies flake8
wemake-python-styleguide(以下、WPSと略します)は、flake8 のプラグインであるため、実行方法は flake8 と同じです。
追加されるエラーコードについては、ドキュメントの Violations を参照してください。