今回はpythonのスタイルガイド、コーディング規約に関連する周辺ツールについてまとめてみました。pep8, pyflakes, flake8, haking, Pylingの5種類のツールについてまとめてあります。
いつもの記事より少し長いため、お急ぎの方はpep8とflake8の章を読むことをおすすめします。
目次
- pep8の使い方
- PEP 8とは
- PEP 8の思想
- pep8を動かしてみよう
- サマリの出力
- 深く見てみる
- pep8のエラーコードの意味とオプション
- pep8のカスタマイズ
- ユーザごとの設定ファイル
- プロジェクトごとの設定ファイル
- pyflakesの使い方
- pyflakesとは
- pyflakesをインストールしてみよう
- pyflakesを動かしてみよう
- pyflakesの設計思想について
- flake8の使い方
- flake8とは
- flake8をインストールしてみよう
- flake8を動かしてみよう
- flake8のエラーコードの意味とオプション
- hackingの使い方
- hackingとは
- hackingをインストールしてみよう
- hackingのエラー検知
- hackingのエラー概要
- hackingのカスタマイズについて
- Pylintの使い方
- Pylintとは
- Pylintをインストールしてみよう
- Pylintを動かしてみよう
- Pylintのエラーコードの意味とオプション
pep8の使い方について
PEP 8とは
pep8の使い方について説明する前に、まずPEP 8について説明します。
pythonにはPEPという文書群があります。
これはpythonコミュニティに対して情報や新機能・プロセス・環境設定などを説明する文書の集合体です。
その設計書のうちの8番目が、PEP 8 - Style Guide for Python Codeです。
Style Guideというタイトルの通り、pythonのコードスタイルについて書かれています。
ちなみに、「Cのコードスタイル」について書かれているのがPEP 7
(PEP 0007 -- Style Guide for C Code _ Python.org)、「Docstringの規約」について書かれているのがPEP 257
(https://www.python.org/dev/peps/pep-0257/)です。
PEP 8の思想
PEP 8の中の一節、A Foolish Consistency is the Hobgoblin of Little Mindsに、コードの一貫性についての考え方について書かれています。この部分は、PEP 8の考え方について理解するのに良いかもしれません。以下に抜粋します。
コードは書かれる時間よりもずっと多くの時間読まれる。
PEP 20 -- The Zen of Pythonも言っている通り、「読みやすさ」は大切なことだ。
このスタイルガイドは一貫性のために存在する。
最も大事なことは、一貫性を壊すときはどんな時かを知ること。例えばこのPEPを守るがために後方互換性を壊すようなことはしてはならない。
このガイドラインであるPEP 8のコードスタイルをチェックするツールがpep8(https://github.com/PyCQA/pep8)です。
このチェッカーは、pythonで実装されています。
pep8を動かしてみよう
さて、前置きが長くなりましたが、pep8
を動かしてみましょう。
以下、特に断りのない限り、各種pythonツールは下記の環境で動作確認しています。
- Mac OSX Yosemite(10.10.3)
- Python 2.7.9
- pip 7.1.2
まずはpep8をインストールしてみましょう。
READMEのとおりに、以下のようなコマンドでインストールできるでしょう。
$ pip install pep8
テストのために、以下のようなファイル(parse.py
)を用意します。
scrapy/scrapyから拝借し、警告が起きるように一部改変しました。
from __future__ import print_function import logging class Command(ScrapyCommand): @property def max_evel(self): levels = self.items.keys() + self.requests.keys() if levels: return max(levels) else: return 0 def add_items(self, lvl, new_items): old_items = self.items.get(lvl, []) self.items[lvl] = old_items + new_items
このファイルに対し、pep8を実行してみます。
基本的な使い方としては $ pep8 [ファイル名またはディレクトリ名]
です
$ pep8 parse.py scrapy/commands/parse.py:3:1: E302 expected 2 blank lines, found 0 scrapy/commands/parse.py:6:5: E303 too many blank lines (2) scrapy/commands/parse.py:7:24: W291 trailing whitespace scrapy/commands/parse.py:8:15: E221 multiple spaces before operator scrapy/commands/parse.py:9:18: E701 multiple statements on one line (colon) scrapy/commands/parse.py:10:13: E701 multiple statements on one line (colon) scrapy/commands/parse.py:15:1: W391 blank line at end of file
今回の場合、7個のエラーが出力されました。
上記とのとおり、エラーが発生したファイル名、位置、エラーコードとその内容がそれぞれ出力されます。
英語で表示されますが、内容は簡潔でわかりやすいと思います。
サマリの出力
サマリを表示するには--statistics -qq
とします。
$ pep8 --statistics -qq ./parse.py 1 E221 multiple spaces before operator 1 E302 expected 2 blank lines, found 0 1 E303 too many blank lines (2) 2 E701 multiple statements on one line (colon) 1 W291 trailing whitespace 1 W391 blank line at end of file
プロジェクトのルートディレクトリで $ pep8 --statistics -qq .
を実行すれば、そのプロジェクトのPEP 8違反箇所のサマリを出力することができるでしょう。
深く見てみる
問題のある箇所を^
を使ってわかりやすく表示することができます。$ --show-source
オプションです。
$ pep8 --show-source ./parse.py ./parse.py:3:1: E302 expected 2 blank lines, found 0 class Command(ScrapyCommand): ^ ./parse.py:6:5: E303 too many blank lines (2) @property ^ ./parse.py:7:24: W291 trailing whitespace def max_evel(self): ^ ./parse.py:8:15: E221 multiple spaces before operator levels = self.items.keys() + self.requests.keys() ^ ./parse.py:9:18: E701 multiple statements on one line (colon) if levels: return max(levels) ^ ./parse.py:10:13: E701 multiple statements on one line (colon) else: return 0 ^ ./parse.py:15:1: W391 blank line at end of file ^
さらに、直し方の説明を見ることも出来ます。
--show-pep8
オプションです。
$ pep8 --show-pep8 ./parse.py ./parse.py:3:1: E302 expected 2 blank lines, found 0 Separate top-level function and class definitions with two blank lines. Method definitions inside a class are separated by a single blank line. Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations). Use blank lines in functions, sparingly, to indicate logical sections. Okay: def a():\n pass\n\n\ndef b():\n pass Okay: def a():\n pass\n\n\n# Foo\n# Bar\n\ndef b():\n pass E301: class Foo:\n b = 0\n def bar():\n pass E302: def a():\n pass\n\ndef b(n):\n pass E303: def a():\n pass\n\n\n\ndef b(n):\n pass E303: def a():\n\n\n\n pass E304: @decorator\n\ndef a():\n pass ....(以下、警告の数だけ説明が出ます)
たくさん出てきてしまいますが、1つ1つドキュメントをチェックせずに治していくことができるので、このオプションも便利そうです。
pep8のエラーコード(Exxx
)の意味とオプション
おおまかな分類としては以下のとおりです(ソースコード内から抜粋)。
- エラーと警告
- E で始まるもの ... errors
- W で始まるもの ... warnings
- 100系 ... indentation
- 200系 ... whitespace
- 300系 ... blank lines
- 400系 ... imports
- 500系 ... line length
- 600系 ... deprecation
- 700系 ... statements
- 900系 ... syntax errors
それぞれの意味の詳細については、こちらのドキュメント、およびソースコード内にのこちらが参考になると思います。
特定のエラーコードをチェック対象から外す
--ignore
オプションでエラーコードを値として与えることで、特定のエラーを無視することが出来ます。
例えば以下のように、カンマ区切りで、チェックをスキップしたいエラーコードを列挙して実行します(E41と書くとE410〜E419は無視されます)。
$ pep8 --ignore=E226,E302,E41 [ファイル名またはディレクトリ名]
pep8のカスタマイズ
ここまで、pep8の走らせ方と、エラーと警告の種類について見てきました。
つぎに、pep8にオプションで渡していた内容を、設定ファイルに書く方法について見ていきましょう。
ユーザごとの設定ファイル
デフォルトのユーザの設定ファイルの場所は'~/.config/pep8'です。
設定ファイルは、オプションと同じように、以下の様な形で書くことが出来ます。
[pep8] ignore = E226,E302,E41 max-line-length = 160
ignore
はカンマ区切りで、チェックをスキップするエラーコードを列挙します(E41と書くとE410〜E419は無視されます。コマンドラインオプションと同じです。)。
max-line-length
は1行の中で許容される最大文字数を指定できます(デフォルトは79で、80文字以上になるとE501エラーが出ます。
設定ファイルの場所は --config=設定ファイルの場所
で指定することも出来ます。
プロジェクトごとの設定ファイル
以上のような設定ファイルを、各プロジェクト内の所定の場所に置くことで、プロジェクト内で共有することが出来ます。
setup.cfg
ファイルまたはtox.ini
ファイルが存在する場合は、そのファイルに記載された内容が読み込まれます。
ちなみに.pep8
ファイルも読み込まれるそうですが、このファイル名を利用するのは非推奨(deprecated)だそうです。
pyflakesの使い方
pyflakesとは
pep8の利用方法を理解したところで、次にpyflakesについて見ていきます。
pyflakesもpep8同様に、pythonのソースコードのエラーチェッカーです。
pyflakesは、pep8とは違い、スタイルについては一切関知せず、論理的なエラーのみを検出します。
pyflakesをインストールしてみよう
以下の様なコマンドでインストールできます。
$ pip install pyflakes
pyflakesを動かしてみよう
基本的な使い方としては、以下のとおりです。
$ pyflakes [ファイル名またはディレクトリ名]
先ほどのparse.pyを対象として動かしてみましょう。
$ pyflakes ./parse.py ./parse.py:2: 'logging' imported but unused ./parse.py:3: undefined name 'ScrapyCommand'
新たに「インポートされたが利用されていないライブラリ」や「未定義の名前」などを検出しました。
pep8でチェックした内容とは違う内容が出てきました。「スタイルについては感知せず、論理的なエラーのみを検出」していることがわかります。
pyflakesの設計思想について
pyflakesの設計思想について、pyflakesのREADMEに説明があります。特徴的なので、簡単に紹介します。
- スタイルについては言及しない。
- 偽陽性を出さないように、並々ならぬ努力をしている。
- 個々のファイルを個別にチェックしているため、PylintやPyCheckerより速く、検出できることが比較的限定されている。
- スタイルのチェックをしたかったり、プロジェクト毎の設定を可能にするには、
flake8
をチェックすると良い。
また、オプションが非常に少なく(--help
と--version
しか無い)、この手のツールが提供するような、「警告を抑制したりカスタマイズする仕組み」がありません。
これらのことも、次に紹介するflake8
が解決します。pyflakes
は、"論理的なエラーのみを検出する"という1つのことをうまくやっているツールと考えることが出来そうです。
flake8の使い方
flake8とは
flake8は、簡単に言うと、「pep8のチェック、pyflakesのチェック、及び循環的複雑度をチェックできるラッパー」です。
他の機能としては、# flake8: noqa
で特定の行の警告を抑制することができる(pyflakes単体だと出来ない)ことや、pep8のように設定ファイルで発生する警告をカスタマイズできる機能などがあります。
flake8をインストールしてみよう
以下の様なコマンドでインストールできます。
$ pip install flake8
flake8を動かしてみよう
使い方及びオプションはpep8
と一緒で $ flake8 [ファイル名またはディレクトリ名]
のように使います。
$ flake8 ./parse.py ./parse.py:2:1: F401 'logging' imported but unused ./parse.py:3:1: E302 expected 2 blank lines, found 0 ./parse.py:3:15: F821 undefined name 'ScrapyCommand' ./parse.py:6:5: E303 too many blank lines (2) ./parse.py:7:24: W291 trailing whitespace ./parse.py:8:15: E221 multiple spaces before operator ./parse.py:9:18: E701 multiple statements on one line (colon) ./parse.py:10:13: E701 multiple statements on one line (colon) ./parse.py:15:1: W391 blank line at end of file
pep8の結果(エラーコードがExxxとWxxxのもの)とpyflakesの結果(エラーコードがFxxxというふうに出てくる)が一緒に出力されていることが確認できます。
これは、pyflakesのエラー(Fxxx)も、pep8同様に、ignoreやselectで設定ファイルで調整できることを意味しています。カスタマイズがやりやすいですね。
flake8のエラーコード(Exxx
)の意味とオプション
flake8のエラーコードは、pep8
で利用されているE***
系とW***
系に加えて、F***
系とC9**
系があります。
E***
/W***
: pep8 のエラー及び警告。F***
: PyFlakes の検知。C9**
: McCabeによる循環的複雑度の検知。
エラーコードの詳細についてはこちらのドキュメントに説明があります。
hackingの使い方
flake8
は拡張性に優れています。次に、プロジェクト独自のルールをflake8に落とし込んだhacking
を紹介します。
hackingとは
hackingは、OpenStack社のOpenStack Style Guidlinesに基づいて作られたflake8
プラグインです。
ソースコードはgithub上にあり、ライセンスはApache License 2.0です。
OpenStack Style Guidlines
の発祥は、こちらのhackingのページによると、
Google Python Style Guide に基づいており、その後OpenStackの独自ルールが追加されたものであるということです。
OpenStack Style Guidlines
によると、hackingには、「いくつかの目的」があり、それは以下の様なものであるということです。
- レビューがつまらない指摘(docstring guidelinesなど)の泥沼に嵌らないように
- 大人数の環境の違いによる読みづらさの排除(unix vs windows newlinesなど)
- 危険なパターンの排除(shadowing built-in or reserved wordsなど)
hackingをインストールしてみよう
$ pip install hacking
flake8が無い状態で実行すると、flake8も一緒にインストールされると思います。
hackingのエラー検知
hackingはこんなことも検知してくれます。H101
を発生させてみます。
サンプルコードに、# TODO いつか直す
というコメントを追加してみます
@property
+ # TODO いつか直す
def max_evel(self):
levels = self.items.keys() + self.requests.keys()
if levels: return max(levels)
hackingがインストールされている状態でflake8を実行すると
$ flake8 scrapy/commands/parse.py scrapy/commands/parse.py:2:1: F401 'logging' imported but unused scrapy/commands/parse.py:3:1: E302 expected 2 blank lines, found 0 scrapy/commands/parse.py:3:15: F821 undefined name 'ScrapyCommand' scrapy/commands/parse.py:6:5: E303 too many blank lines (2) scrapy/commands/parse.py:7:7: H101 Use TODO(NAME) scrapy/commands/parse.py:8:24: W291 trailing whitespace scrapy/commands/parse.py:9:15: E221 multiple spaces before operator scrapy/commands/parse.py:10:18: E701 multiple statements on one line (colon) scrapy/commands/parse.py:11:13: E701 multiple statements on one line (colon) scrapy/commands/parse.py:17:1: W391 blank line at end of file
scrapy/commands/parse.py:7:7: H101 Use TODO(NAME)
が新たに発生しました。
これは、ガイドラインのここにかかれているルールで、
[H101] Include your name with TODOs as in # TODO(yourname). This makes it easier to find out who the author of the comment was.
というルールのためです(細かいですね^^)。
このように、hackingをインストールすると、flake8を実行した時にhacking独自のルール(エラーコード:Hxxxの形です)が追加されます。
hackingのエラー概要
hackingのエラーコードについてのドキュメントを探したのですが、見つかりませんでした(エラーコードについてご存知の方は、教えてください)。
ソースコードを読む限り、以下のように分類できると思います。
H1xx
系
ライセンスやコメントなどについて。
[H101] Include your name with TODOs as in ``# TODO(yourname)``. This makes it easier to find out who the author of the comment was. [H102 H103] Newly contributed Source Code should be licensed under the Apache 2.0 license. All source files should have the following header:: [H104] Files with no code shouldn't contain any license header nor comments, and must be left completely empty. [H105] Don't use author tags. We use version control instead. [H106] Don't put vim configuration in source files (off by default).
H2xx
系
テストなどについて。
[H201] Do not write ``except:``, use ``except Exception:`` at the very least. [H202] Testing for ``Exception`` being raised is almost always a ...
H23x
系
Python3.xとの互換性。
[H231] ``except``. Instead of:: [H232] Python 3.x has become more strict regarding octal string [H233] The ``print`` operator can be avoided by using:: [H234] ``assertEquals()`` logs a DeprecationWarning in Python 3.x, use [H235] ``assert_()`` is deprecated in Python 3.x, use ``assertTrue()`` instead. [H236] Use ``six.add_metaclass`` instead of ``__metaclass__``. [H237] Don't use modules that were removed in Python 3. Removed module list: [H238] Old style classes are deprecated and no longer available in Python 3
H3xx
系
import
のスタイルについて。
[H301] Do not import more than one module per line (*) [H303] Do not use wildcard ``*`` import (*) [H304] Do not make relative imports [H306] Alphabetically order your imports by the full module path.
H4xx
系
Docstrings
について。
[H401] Docstrings should not start with a space. [H403] Multi line docstrings should end on a new line. [H404] Multi line docstrings should start without a leading new line. [H405] Multi line docstrings should start with a one line summary followed
H5xx
系
スタイルの統一などについて。
[H501] Do not use ``locals()`` or ``self.__dict__`` for formatting strings, [H702] If you have a variable to place within the string, first [H703] If you have multiple variables to place in the string, use keyword [H903] Use only UNIX style newlines (``\n``), not Windows style (``\r\n``)
hackingのカスタマイズについて
flake8
のプラグインですので、pep8
と同様にエラーコードを指定してのignoreなどが可能です。
例えば、上述したとおりH1**
系のエラーは、「Apache License2.0
の明示」や、「TODOコメントの書き方」など、会社単位もしくはプロジェクト単位で異なるものが多いと考えられるので、まずはignoreしてみても良いかもしれません。
Pylintの使い方
ここまで個々のファイルを個別にチェックできるpep8やpyflake系のツールを見てきましたが、
最後に、複数ファイルにまたがったモジュールやパッケージをチェックできるツールの1つである、Pylintをご紹介します。
Pylintとは
Pylintもまた、pythonのコードチェッカーの1つです。
最初に紹介したシンプルなチェックツールであるpep8
も歴史がある(2006-)のですが、Pylintも歴史が古いようです(最初のリリース0.0版が2003年)。
pep8と比較すると、多彩なチェック項目と、オプションの多さが特徴です。
2015年11月現在も頻繁にコミットがされているようです。ソースコードはこちら(https://bitbucket.org/logilab/pylint)です。
Pylintをインストールしてみよう
他のツールと同様、pipでインストールすることが出来ます。
$ pip install pylint
Pylintを動かしてみよう
使い方としては $ pylint [オプション] [モジュールまたはパッケージ]
になります。
前述のhacking
で利用したparse.py
をサンプルとして動かしてみましょう。結果は、以下のようになりました。
$ pylint scrapy/commands/parse.py No config file found, using default configuration ************* Module scrapy.commands.parse C: 7, 0: Trailing whitespace (trailing-whitespace) C: 8, 0: Exactly one space required before assignment levels = self.items.keys() + self.requests.keys() ^ (bad-whitespace) C: 1, 0: Missing module docstring (missing-docstring) C: 3, 0: Missing class docstring (missing-docstring) W: 3, 0: Class has no __init__ method (no-init) E: 3,14: Undefined variable 'ScrapyCommand' (undefined-variable) C: 7, 4: Missing method docstring (missing-docstring) E: 8,18: Instance of 'Command' has no 'items' member (no-member) E: 8,38: Instance of 'Command' has no 'requests' member (no-member) C: 9,19: More than one statement on a single line (multiple-statements) C: 12, 4: Missing method docstring (missing-docstring) E: 13,20: Instance of 'Command' has no 'items' member (no-member) E: 14, 8: Instance of 'Command' has no 'items' member (no-member) W: 2, 0: Unused import logging (unused-import) ...(以下レポートが出ますが長いので割愛します)
hacking
では計9個の警告が出ていましたが、今回は多少の増減があり、計12個の警告が出ました。
特徴的なものとして、
E: 8,18: Instance of 'Command' has no 'items' member (no-member)
のような警告が新たに検出されました。
これは、チェック時にitems
というメンバが見つからなかったという警告(エラーコードはE1101)です。
ただし、公式wikiのE1101のページを見ると、動的にメンバを追加した時には偽陽性を出すことがある、ということが書かれています。
なので、意図的にそのような書き方をしている際には注意が必要です。
Pylintのエラーコードの意味とオプション
Pylintは、多くのエラーコードおよびオプションがありますので、全ては紹介しきれません。
公式ドキュメントのこのページで一覧化されていますので、カスタマイズする際はこちらを参考にすると良いと思います。
今回は、チェックを並列化するオプションと設定ファイルの生成方法のみご紹介します。
チェックの並列化による高速化(-jオプション)
Pylintの特徴として、個々のファイルを個別にチェックするのでなく、パッケージやモジュール単位で評価するという特徴があるため、pyflakesやpep8に比べてどうしてもチェックに時間がかかってしまいます。
-j(--jobs)
オプションで、並列化することが出来るようです。
-j <n-processes>, --jobs=<n-processes> Use multiple processes to speed up Pylint. [current:1]
設定ファイルの生成方法(--generate-rcfileオプション)
--generate-rcfileオプションで、標準出力に設定ファイル(pylintrc
)の雛形を出力することが出来ます。
特にオプションで設定ファイルの場所を渡さないかぎり、カレントディレクトリのpylintrc
が最優先で読み込まれます。
プロジェクト毎に共有ルールを決める場合は、プロジェクトのルートディレクトリにpylintrc
を置くと良いかもしれません。
最後に
今回はpythonのスタイルガイド、コーディング規約に関連する周辺ツールについてまとめてみました。
pep8やflake8に関するツールを見てきましたが、いずれにしても実際にプロジェクトに合ったカスタマイズをすることで開発がやりやすくなると思います。
flake8がpep8をwrappingしていたり、hackingがflake8のプラグインとして作られているなど、最も普及しているようです。ぜひまずはflake8をお試し下さい。
また、SideCIでもflake8をお使いいただけます。