デコレータを外す
おはこんばんちわ。情弱王子feizです。
最近とある方の影響で自動テストに凝っておりまして、それはもうセルフプレジャーを覚えた猿のようにテストを書いてるとかいないとかな毎日です。
さて
Pythonでテストをかいてると、たまにデコレータが邪魔になることがあります。
例えばjsonを返すAPIのビューをdjangoでこんなふうにかきました。
class Api(object): @require_GET @json_response @error_handle @validate(MyViewForm) def myview(self, request, params): return { "azuma": params["feiz"], "okano": params["tokibito"], }
- require_GET
- リクエストメソッドをチェック
- json_response
- 辞書をjson.dumpsしてHTTPResponseに加工してヘッダとか設定
- error_handle
- Exceptionをキャッチしてエラーを示す辞書を返す
- validate(Form)
- リクエストパラメータをFormで検証して、cleaned_dataをparamsとしてviewに渡す
とまあjsonを返すのに必要な処理の大半をデコレータに投げてしまえば、viewの方は非常に簡潔に辞書だけ返せばいいよみたいな実装にできるわけです。
問題
このビューの出力をテストするとなると、理想的にはmyviewが返す辞書を検証出来れば万々歳なんですが、当然デコレータがかかっているのでmyviewを呼び出すとHttpResponseが返ります。
これをまたjson.loadsでパースしてうんたらかんたらというのはちょっとおばかすぎる。なんとか任意のタイミングでデコレータをなかった事にしたい。
がんばる
色々調べるとこんな記事が。
デコレータ式を適用した関数から元の関数名を探す - gumi Engineer’s Blog
どうやら関数オブジェクトのfunc_closure(python2.6からは__closure__でも同じものが得られる)というフィールドを覗くとクロージャの中身(?)が見れるらしい。情弱過ぎてさっぱり理解が追いついていない。
とにかくごちゃごちゃと試行錯誤した結果が以下のコード。
import types def strip_decorators(func): naked = func while naked.func_closure is not None: for cell in naked.func_closure: cell = cell.cell_contents if type(cell) is types.FunctionType: naked = cell break return naked def deco1(func): def _deco1(): print "deco1" return func() return _deco1 def deco2(msg): def _deco2(func): def _func(): print msg return func() return _func return _deco2 @deco1 @deco2("jo_jaku") def feiz(): print "feiz" print "call feiz()" feiz() naked_feiz = strip_decorators(feiz) print "\ncall naked_feiz()" naked_feiz()
出力
call feiz() deco1 jo_jaku feiz call naked_feiz() feiz
はずせたっぽいですね。
理解出来ていないところ
とりあえずそれっぽいことができたのはいいんですが、やっぱりきっちり理解しておきたい。
なので今のところ理解できてない部分をまとめておく。
- bold;">func_closureの中のcellってオブジェクトはなんだ: とある方が教えてくれた記事を参考にする。 -> http://d.hatena.ne.jp/atsuoishimoto/20101006/1286338675
- bold;">ほんとにこのコードで完璧に外せるのか: ↑の記事を理解するか、いろんなパターンをためしまくる。
- bold;">bound methodにstrip_decoratorsを適用するとfunctionになって返ってくるのはなぜか: bound methodはどこいった?
つづく
1/50