前の映画情報取得ツールを製作していた時のこと。
OMDB APIを取得するツールをpythonで自作する - paloma blog
全処理を一旦書き上げたのですが実行しても出力されません。
(movies) masashi@PC-ubuntu:~/tests$ python getinfo.py (movies) masashi@PC-ubuntu:~/tests$
しかしプロンプト内でやるとちゃんと返ってきます。
>>> main() Title: Argo Year: 2012 Rated: R Released: 12 Oct 2012 Runtime: 120 min
どうにもわからないのでデバッグをすることにしました。
pdbモジュール
よくやるデバッグと言えばprintを仕込んで変数内容を確認するというものですが、
私もpython歴数年になりますのでそろそろデバッグもスマートに行いたいところです。
pdbモジュールの記事がよく出てくるのでこちらを使ってデバッグしてみます。
pdb --- Python デバッガ — Python 3.9.4 ドキュメント
モジュール読み込み
コードの中に仕込む方法が公式ドキュメントなどで出てきますが、引数で読み込ませる方法のほうが簡単そうだったのでこちらで試してみます。
-m pdbでモジュールを読み込ませてコードを起動します。
(movies) masashi@PC-ubuntu:~/tests$ python -m pdb getinfo.py > /home/masashi/tests/getinfo.py(1)<module>() -> import requests (Pdb)
するとこんな感じでpdbの対話モード入ります
ヘルプ
(Pdb) h Documented commands (type help <topic>): ======================================== EOF c d h list q rv undisplay a cl debug help ll quit s unt alias clear disable ignore longlist r source until args commands display interact n restart step up b condition down j next return tbreak w break cont enable jump p retval u whatis bt continue exit l pp run unalias where Miscellaneous help topics: ========================== exec pdb
pdbを使うのに戸惑う理由がオプションの多さだと思います。
どれを使えばよいか迷いますが、とりあえずは変数が想定通り格納されているかという観点で考えると
p(rint)だけ使えばOKということになります。
ちなみコードはこちら
llで全行出力されます。
長文のコードだと使えないかもしれませんが今回のは短いです。
テスト用にちょっといじってあります。
実はこの時点でおかしいところがわかりますね。
(Pdb) ll 1 -> import requests 2 import sys 3 import json 4 # import configure 5 import re 6 7 # KEY = configure.APIKEY 8 # url = 'http://www.omdbapi.com/' 9 # gettitle = '+'.join(sys.argv[1:]) 10 # getinfo = ''.join([url, '?apikey=', KEY, '&t=', gettitle]) 11 with open('sample.json', 'r') as f: 12 movieinfo = f.read() 13 14 def main(): 15 16 dataload = json.loads(movieinfo) 17 datadump = json.dumps(dataload, indent=0) 18 19 shapedata = re.sub('\n.\n|"|{|}|\[|\]', "", datadump) 20 shapedata = re.sub(',\n', "\n", shapedata) 21 22 print(shapedata) 23 24 if __name__ == 'main': 25 main()
ちなみにlだと現在の行周辺が出力されます。
-> import sys (Pdb) l 1 import requests 2 -> import sys 3 import json 4 # import configure 5 import re 6 7 # KEY = configure.APIKEY 8 # url = 'http://www.omdbapi.com/' 9 # gettitle = '+'.join(sys.argv[1:]) 10 # getinfo = ''.join([url, '?apikey=', KEY, '&t=', gettitle]) 11 with open('sample.json', 'r') as f:
n(ext)
nは1行ずつ読み込みます。
空エンターでも同じ挙動をしますね。
(Pdb) n > /home/masashi/tests/getinfo.py(2)<module>() (Pdb) n > /home/masashi/tests/getinfo.py(3)<module>() -> import json (Pdb) > /home/masashi/tests/getinfo.py(5)<module>() -> import re (Pdb) > /home/masashi/tests/getinfo.py(11)<module>() -> with open('sample.json', 'r') as f: (Pdb) > /home/masashi/tests/getinfo.py(12)<module>() -> movieinfo = f.read() (Pdb) > /home/masashi/tests/getinfo.py(14)<module>() -> def main():
p(rint)
pで変数を出力できます。
被疑の行まで来たらpで中身を確認すれば所謂printのデバッグになりますね。
今回はサンプルのjsonデータを読み込ませてますが、ちゃんと格納されてます。
(Pdb) p movieinfo '{"Title":"Argo","Year":"2012","Rated":"R","Released":"12 Oct 2012","Runtime":"120 min","Genre":"Biography, Drama, Thriller","Director":"Ben Affleck","Writer":"Chris Terrio, Tony Mendez, Joshuah Bearman","Actors":"Ben Affleck, Bryan Cranston, John Goodman", ...
main実行の行まで来たのでここで手動で実行してみます。
> /home/masashi/tests/getinfo.py(24)<module>()->None -> if __name__ == 'main': (Pdb) main() Title: Argo Year: 2012 Rated: R Released: 12 Oct 2012 Runtime: 120 min ...
ちゃんと出力できてますね。
となるとターミナルから起動、出力周りが怪しいということになりますね。
原因
24 -> if __name__ == 'main': 25 main()
mainを実行されるときの行がミスしてました。
mainは__main__
と書かないと認識されませんね。
ここは久しぶりに空で書いたのでルールを忘れてました。
というわけで対象行を修正して無事にデバッグ完了です。
24 -> if __name__ == '__main__': 25 main()
(movies) masashi@PC-ubuntu:~/tests$ python getinfo.py Title: Argo Year: 2012 Rated: R Released: 12 Oct 2012 Runtime: 120 min ...
おまけ r(eturn)
nだと1行ずつで時間がかかってしまいましが、rだと「現在の関数が返るまで実行を継続」する様です。
現在の関数が何を指しているか不明ですが私の場合だとmain関数のところまで飛びました。
-> import requests (Pdb) l 1 -> import requests 2 import sys 3 import json 4 # import configure 5 import re 6 7 # KEY = configure.APIKEY 8 # url = 'http://www.omdbapi.com/' 9 # gettitle = '+'.join(sys.argv[1:]) 10 # getinfo = ''.join([url, '?apikey=', KEY, '&t=', gettitle]) 11 with open('sample.json', 'r') as f: (Pdb) r --Return-- > /home/masashi/tests/getinfo.py(24)<module>()->None -> if __name__ == 'main': (Pdb) l 19 shapedata = re.sub('\n.\n|"|{|}|\[|\]', "", datadump) 20 shapedata = re.sub(',\n', "\n", shapedata) 21 22 print(shapedata) 23 24 -> if __name__ == 'main': 25 main() [EOF]
まとめ
というわけで初めてpdbを使ったデバッグを行いました。
いつもprintを仕込んでいましたが、コードを汚さずにデバッグできるのは便利ですね。
とりあえずpだけ覚えておけば私の規模のコードだと問題なくできました。
pdbには他にもいろいろオプションがあるので、また詰まる時が来たら他のも使ってみたいと思います。
関数の中に入るコマンドもあった記憶がありますが、今回は確認できなかったので次回チャレンジですね。