Pythonでtypoを素早く察知する方法
そこで「先日公開したCodeHackモジュールを使えば簡単にできるよ」と言いながら作ったのが今回紹介するモジュールです。
CodeHack 0.05に同梱しました。
CodeHack 0.06に同梱しました。
使い方は簡単です。 たとえば下のような関数fooを定義したとします。
def foo(left, top, right, bottom): "function for test" width = right - left hieght = top - botom print height, widthよく見ると2カ所ほど綴り間違いがあるのですが、 この関数をcodehack.var_warningの中のcheckfuncに渡してみると 下のようにその綴り間違いの変数が表示されます。関数fooを実行していないので、たとえ実行に1時間かかるような関数であってもチェックは一瞬です。
>>> from codehack.var_warning import checkfunc >>> checkfunc(foo) variable 'botom' is global variable 'height' is global variable 'hieght' is not used variable 'bottom' is not used実は綴り間違いの判定は何もしていなくて、 単純に「値が代入されたのに読み出されていない」 という変数を「not used」と表示しているだけです。 また、逆の「値が代入されていないのに読み出されている変数」は Pythonでは「グローバル変数と見なして外のスコープを見に行く」 という仕様になっているのでglobalと表示しています。(注1)
ただ、そのままですと'range'や'set'なども警告されたり、 自分で定義したグローバル関数を使うとそれも警告されたり、 とうっとうしいことになるので、 警告しない名前のリストをsurpressという引数で指定できるようにしてあります。 デフォルトでは組み込みの名前(rangeとか)がsurpressに入っています。
def checkfunc(func, print_globals = True, print_not_used = True, print_func_name = False, surpress = _default_surpress):
CodeHackモジュールを使っている部分は、実はたいしたことはしていません。 下のようにLOAD_FAST(ローカル変数のロード)という オペコードの引数(変数名になっている)を集めているだけです。 codehack.util.analyzeはオペコード間の依存関係(どのオペコードが積んだ値をどのオペコードが消費するか)のトレースをするのですが、それも必要ないのでわざわざ外しています。
import util opcodes = util.analyze(func, do_trace=False) loaded_vars = set(op.argstr for op in opcodes if op.opname == "LOAD_FAST")下のようにして、モジュールの中の全ての関数をチェックすることもできます。 すでに自分で書いたモジュールがあれば試してみると面白いでしょう。 checkallはprint_globalsをFalseにしてあるので「代入されているけど読み出されていない」変数だけが出ます。
>>> from codehack.var_warning import checkfunc, checkall >>> import urllib >>> checkfunc(urllib.urlopen) variable 'FancyURLopener' is global variable '_urlopener' is global >>> checkall(urllib.__dict__) * function splitnport * function getproxies * function retrieve variable 'msg' is not used variable 'garbage' is not used (snip)ぜひ試してみて、ご意見ご感想やパッチを頂けるとうれしいです。
(注1) 実際はもちろんグローバルの名前空間にいきなり行くわけではなくて、 スコープがネストしている場合には1枚外のスコープへ行きます。 globalと呼んでいるのは単にバイトコードがLOAD_GLOBALだからだったりします。 いい名称があれば変えたほうがいいかもしれません。 また、関数がネストしている場合は内側の関数で使われる変数を 「使われる」と判断することができずに警告を出してしまいます。 これはdisしてみるとわかるのですが、内側の関数はコードオブジェクトとして 定数テーブルに埋め込まれているため、 外側の関数のバイトコードを見ただけでは内側で何を使うかがわからない ことが原因です。定数テーブルにコードオブジェクトがあるかどうかをチェックして 再帰的にたどるようにすればいいだろうと思いますが…それは今後の反響次第と言うことで。
フィードバック
codehack0.6を使っているのですが、Unicodeの日本語定数が自コードに記述されていると、codehack/definition.pyの41行目の
でstrに変換できずにエラーとなるようです。 (self.cobj.co_consts[self.arg]にUnicodeの日本語文字列が来る。) ※自コードはeuc-jpで書かれており、ちょうど以下の部分の"""で囲まれた引数をcodehackが処理しようとして落ちています。 今のところUnicodeの場合は適当に別処理にするように修正して手元で使っていますが、codehackの仕組みがよくわかっていないのでお時間のあるときに公式な修正をよろしくおねがいしますっ!