読者です 読者をやめる 読者になる 読者になる

CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: pylint でバージョン 3.x 非互換のシンタックスを見つけ出す

Python

先日の記事で Python 2.x/3.x の互換性に関するツールをいくつか紹介したけど、詳しい使い方までは書くことができなかった。 今回は、その中のひとつ pylint (の --py3k オプション) について紹介してみる。

blog.amedama.jp

pylint とは

pylint は Python で書かれたソースコードを静的に解析して、使われていない箇所 (デッドコード) や、あるいは不具合の元となるような危険な記述を見つけ出して教えてくれるサードパーティ製のツール。 Python を書くのであれば、エディタや CI サーバなどに組み込んで是非ともかけておきたいもののひとつ。

最近になって、この pylint に --py3k というオプションが追加された。 Python 2.x と 3.x では、使用できるシンタックスが微妙に異なっていて、中には 3.x では動作しないものがある。 その多くは 2.x の中でも古いバージョンで使われていたものなんだけど、pylint の --py3k オプションはそれを見つけて警告してくれる。

pylint をインストールする

pylint はサードパーティ製のパッケージなのでパッケージマネージャ (easy_install や pip) を使ってインストールする。

$ pip install pylint

インストールすると pylint コマンドが使えるようになる。 ちなみに --py3k オプションを使う場合、インストール先の Python 実行環境は 2.x じゃないとだめっぽい。

$ pylint --version
No config file found, using default configuration
pylint 1.4.4, 
astroid 1.3.8, common 1.0.2
Python 2.7.10 (default, Jul 14 2015, 19:46:27) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)]

pylint を使ってみる

それでは早速 pylint の --py3k オプションを使って Python 3 非互換のシンタックスをソースコードの中から見つけてみよう。 次のファイルでは print を文として使ってしまっているため 3.x では動作しない。

$ cat << EOF > sample.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-


def main():
    print 'Hello, World!'


if __name__ == '__main__':
    main()
EOF

Python 3.4 で実行してみてもエラーになる。

$ python --version
Python 3.4.3
$ python sample.py
  File "sample.py", line 6
    print 'Hello, World!'
                        ^
SyntaxError: Missing parentheses in call to 'print'

それでは、このソースコードを pylint にかけてみよう。

$ pylint --py3k sample.py | head
No config file found, using default configuration
************* Module sample
E:  6, 4: print statement used (print-statement)


Report
======
5 statements analysed.

Statistics by type
------------------

目論見通り「print 文が使われてるぞこらー!」とエラーになった。

ちなみに、結構細かい仕様変更でも見つけてくれるみたい。 次のソースコードでは 2.x と 3.x で返り値の型が変更になった '/' 演算子をそのまま使っている。

$ cat << EOF > sample.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import print_function


def main():
    print(1 / 1)


if __name__ == '__main__':
    main()
EOF

pylint にかけてみよう。

$ pylint --py3k sample.py | head
No config file found, using default configuration
************* Module sample
W:  8,10: division w/o __future__ statement (old-division)


Report
======
6 statements analysed.

Statistics by type
------------------

おおー、「'/' 演算子使うなら future.division をインポートしなきゃダメじゃん!」と警告してくれている。

ツールの限界

ただ、残念ながらチェックできるのはシンタックスレベルまでで、標準ライブラリ周りの変更までは調べきれないようだ。 次のソースコードでは 3.x で名前が異なる標準ライブラリの urlparse モジュールを使っている。

$ cat << EOF > sample.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import absolute_import
from __future__ import print_function
from urlparse import urlparse


def main():
    o = urlparse('http://example.jp')
    print('netloc: {netloc}'.format(netloc=o.netloc))


if __name__ == '__main__':
    main()
EOF

これも pylint にかけてみる。

$ pylint --py3k sample.py | head
No config file found, using default configuration


Report
======
9 statements analysed.

Statistics by type
------------------

+---------+-------+-----------+-----------+------------+---------+

残念ながら見つけることはできなかった。

まとめ

今回は pylint を使って Python 3 非互換のシンタックスをソースコードの中から見つけ出すやり方を紹介した。 Python に慣れないうちは、過去のブログ記事などを参考にしたりなんかで意図せず古いシンタックスを使ってしまうようなことがある。 そういうのは指摘してもらわない限りなかなか気づかないもので、いざ必要となったときにげってなるのがありがち。 そうした意味で、Python 3 非互換に限らず推奨されない書き方をしたときに教えてくれる pylint は大変貴重な存在といえる。 元々 pylint はソースコードの品質をチェックするためにかけておくのが基本って感じなので、ついでに --py3k しておくのがいいんじゃないかと思う。