GoogleAppEngine の Xcode プロジェクトを PyChecker でエラーチェックする
私の使っている MacOSX 10.6 はデフォルトで python2.6 を使用するため、GoogleAppEngine の python2.5 準拠のプロジェクトで問題なく動作させるために設定した項目をいくつか書きます。
- PyChecker について
Google Python スタイルガイド で以下のように記述されています。
定義:
Pychecker は Python のソースコード内のバグを発見するためのツールです。 C や C++ のような、非動的言語のコンパイラが引き起こすような問題を発見します。 lint とよく似ています。 Python の動的な特性により、いくつかの警告は正確ではない可能性があります - とはいえ、不正確な警告はほとんどありません。
利点:
タイプミス、未初期化変数の利用などの単純ミスを発見することができます。
欠点:
pychecker は完璧ではありません。利用するために、a) コードを書き b) 警告を無効にし c) 改良するか d) 無視する 等が必要になることがあります。
PyChecker はホームページからダウンロードできます。
- PyChecker の使用法
コマンドラインで PyChecker を使用するには、以下の二通りの方法があります。
# インストールされている PyChecker を実行する場合 pychecker [options] file1.py file2.py ... # ソースコード (pychecker-0.8.19/pychecker/checker.py) から PyChecker を実行する場合 python checker.py [options] file1.py file2.py ...
今回は、好きな python のバージョンから実行できるように、ソースコードから実行する方法を用います。
ちなみに、PyChecker はチェックするファイルを file1.py file2.py ... というように、まとめて指定する必要があるようです。
- PyChecker のオプションと例
コマンドラインで pychecker の -h オプションを用いると、以下のオプションが閲覧できます。
Usage for: checker.py [options] PACKAGE ... PACKAGEs can be a python package, module or filename Long options can be preceded with no- to turn off (e.g., no-namedargs) Category Options: Change warning for ... [default value] Major Options: --only only warn about files passed on the command line [off] -e, --level the maximum error level of warnings to be displayed -#, --limit the maximum number of warnings to be displayed [10] -F, --config specify .pycheckrc file to use --quixote support Quixote's PTL modules --evil list of evil C extensions that crash the interpreter [[]] --keepgoing ignore import errors [off] Error Control: -i, --import unused imports [on] -k, --pkgimport unused imports from __init__.py [on] -M, --reimportself module imports itself [on] -X, --reimport reimporting a module [on] -x, --miximport module does import and from ... import [on] -l, --local unused local variables, except tuples [on] -t, --tuple all unused local variables, including tuples [off] -9, --members all unused class data members [off] -v, --var all unused module variables [off] -p, --privatevar unused private module variables [on] -g, --allglobals report each occurrence of global warnings [off] -n, --namedargs functions called with named arguments (like keywords) [off] -a, --initattr Attributes (members) must be defined in __init__() [off] -I, --initsubclass Subclass.__init__() not defined [off] -u, --callinit Baseclass.__init__() not called [on] -0, --abstract Subclass needs to override methods that only throw exceptions [on] -N, --initreturn Return None from __init__() [on] -8, --unreachable unreachable code [off] -2, --constCond a constant is used in a conditional statement [on] -1, --constant1 1 is used in a conditional statement (if 1: or while 1:) [off] --stringiter check if iterating over a string [on] --stringfind check improper use of string.find() [on] -A, --callattr Calling data members as functions [off] -y, --classattr class attribute does not exist [on] -S, --self First argument to methods [self] --classmethodargs First argument to classmethods [['cls', 'klass']] -T, --argsused unused method/function arguments [on] -z, --varargsused unused method/function variable arguments [on] -G, --selfused ignore if self is unused in methods [off] -o, --override check if overridden methods have the same signature [on] --special check if __special__ methods exist and have the correct signature [on] -U, --reuseattr check if function/class/method names are reused [on] -Y, --positive check if using unary positive (+) which is usually meaningless [on] -j, --moddefvalue check if modify (call method) on a parameter that has a default value [on] --changetypes check if variables are set to different types [off] --unpack check if unpacking a non-sequence [on] --unpacklen check if unpacking sequence with the wrong length [on] --badexcept check if raising or catching bad exceptions [on] -4, --noeffect check if statement appears to have no effect [on] --modulo1 check if using (expr % 1), it has no effect on integers and strings [on] --isliteral check if using (expr is const-literal), doesn't always work on integers and strings [on] --constattr check if a constant string is passed to getattr()/setattr() [on] Possible Errors: -r, --returnvalues check consistent return values [on] -C, --implicitreturns check if using implict and explicit return values [on] -O, --objattrs check that attributes of objects exist [on] -7, --slots various warnings about incorrect usage of __slots__ [on] -3, --properties using properties with classic classes [on] --emptyslots check if __slots__ is empty [on] -D, --intdivide check if using integer division [on] -w, --shadow check if local variable shadows a global [on] -s, --shadowbuiltin check if a variable shadows a builtin [on] Security: --input check if input() is used [on] -6, --exec check if the exec statement is used [off] Suppressions: -q, --stdlib ignore warnings from files under standard library [off] -b, --blacklist ignore warnings from the list of modules [['Tkinter', 'wxPython', 'gtk', 'GTK', 'GDK']] -Z, --varlist ignore global variables not used if name is one of these values [['__version__', '__warningregistry__', '__all__', '__credits__', '__test__', '__author__', '__email__', '__revision__', '__id__', '__copyright__', '__license__', '__date__']] -E, --unusednames ignore unused locals/arguments if name is one of these values [['_', 'empty', 'unused', 'dummy']] --missingattrs ignore missing class attributes if name is one of these values [[]] --deprecated ignore use of deprecated modules/functions [on] Complexity: -L, --maxlines maximum lines in a function [200] -B, --maxbranches maximum branches in a function [50] -R, --maxreturns maximum returns in a function [10] -J, --maxargs maximum # of arguments to a function [10] -K, --maxlocals maximum # of locals in a function [40] -5, --maxrefs maximum # of identifier references (Law of Demeter) [5] -m, --moduledoc no module doc strings [off] -c, --classdoc no class doc strings [off] -f, --funcdoc no function/method doc strings [off] Debug: --rcfile print a .pycheckrc file generated from command line args -P, --printparse print internal checker parse structures [off] -d, --debug turn on debugging for checker [off] --findevil print each class object to find one that crashes [off] -Q, --quiet turn off all output except warnings [off] -V, --version print the version of PyChecker and exit
例えば、--stdlib オプションを有効 [on] にしたい場合、--stdlib とだけ書きます。
また、--deprecated オプションを無効 [off] にしたい場合、--no-deprecated と書きます。
私がある AppEngine プロジェクトで使用しているオプションの組み合わせは以下の通りです。
実行時はコメントを削除してください。
python2.5 checker.py \ --stdlib \ # --stdlib [on] --blacklist null,BeautifulSoup,oauth,sgmllib,markupbase \ # --blacklist ['null', 'BeautifulSoup', 'oauth', 'sgmllib', 'markupbase'] --no-override \ # --override [off] --unusednames cls \ # --unusednames ['cls'] $FILES # file1.py file2.py ...
# オプションの説明 --stdlib # Python 標準ライブラリ内のエラーを無視する --blacklist # 外部・オープンソースモジュールのエラーを無視する --no-override # 継承したクラスでメソッドのオーバーライドを許可する --unusednames # 引数が未使用でもよいもの(クラスメソッドの cls 等)
全てのオプションを指定した後ろに --rcfile を追加すると、オプション変更後の .pycheckrc ファイルの内容が出力されます。
- セットアップ for Python2.5
前述したように、YAML, WebOb, Django 等のサードパーティライブラリはデフォルトの Python2.6 (/Library/Python/2.6/site-packages) に対してインストールされています。
これを Python2.5 (/Library/Python/2.5/site-packages) からでも参照できるように、シンボリックリンクを作成します。
# YAML のシンボリックリンクを作成 ln -s /Library/Python/2.6/site-packages/yaml/ /Library/Python/2.5/site-packages/yaml # Django のシンボリックリンクを作成 ln -s /Library/Python/2.6/site-packages/django/ /Library/Python/2.5/site-packages/django
WebOb は WebOb-0.9-py2.6.egg となっていて、Python2.6 専用のものがインストールされてしまっているため、新たに Python2.5 に対してインストールさせます。
AppEngine では WebOb 0.9 なのですが、なかなか見つけられなかったので最新バージョンの 1.0.6 で代用しました。 エラーチェック用なので、API が変更されていなければ問題ないはず・・・
python2.5 setup.py install # WebOb-1.0.6-py2.5.egg がインストールされる
追記)SDK コードに WebOb 0.9 が入っていました。
GoogleAppEngine の google モジュールについても、 Python2.6 にしかシンボリックリンクが作成されていなかったため、このシンボリックリンクを Python2.5 にリンクさせました。
ln -s /Library/Python/2.6/site-packages/google/ /Library/Python/2.5/site-packages/google
ちなみに MacOSX の場合、google/ 本体は /Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google にあります。
これで PyChecker が GoogleAppEngine ライブラリに対して出す ImportError を解決できるでしょう。
- Xcode のターゲット設定
PyChecker にソースコードを直接 検証させて、何か問題が生じたら嫌なので、Xcode のプロダクトディレクトリにコピーしたものをエラーチェックさせます。
PyChecker を実行するシェルスクリプトは以下のようになります。
cd $BUILT_PRODUCTS_DIR # プロダクトディレクトリに移動 FILES="" for f in *.py do FILES="$FILES $f" # チェックするファイル名をスペース区切りで結合 done # PyChecker ソースコードのパス PYCHECKER=/Users/User/Documents/Web_Project/Python-Libraries/pychecker-0.8.19/pychecker/checker.py python2.5 $PYCHECKER \ --stdlib \ --blacklist null,BeautifulSoup,oauth,sgmllib,markupbase \ --no-override \ --unusednames cls \ $FILES exit 0
python2.5 でエラーチェックさせることで、md5 等の deprecation 警告に悩まされることもなくなります。
外部ライブラリでエラーが出た場合、--blacklist にファイル名を追加してください。
- 追記
Pychecker を OS に再インストールしたりすると、google.appengine.ext.webapp.__init__ の警告が消えてくれることもある。 ソースコードから実行しているのに不思議。 OS 側の stdlib 設定が書き変わるから?
Google App Engine SDK をインストールし直して変になった時は、試してみてください。
# pychecker-0.8.19
sudo python2.5 setup.py install
- 追記
一度 OS 標準の Python(2.6) で PyChecker を実行した後に、再び Python2.5 で実行すると google ライブラリのエラーチェックを無視することができました。
PYTHONPATH (sys.path で確認できる) の追加(以下)は効果ありませんでした。
export PYTHONPATH=$PYTHONPATH:/Library/Python/2.5/site-packages