問題
python3ではmultiprocessingライブラリを使うと、例えば次のように簡単に並列処理が実現できます。
test.py
from multiprocessing import Pool
def f(x):
return x*x
if __name__ == '__main__':
p = Pool(5) #数字分プロセス数を確保
print(p.map(f, [1, 2, 3, 4, 5])) #並列処理したい関数とその引数を指定
実行すると、次のように並列処理された結果が出力されます。
$ python3 test.py
[1, 4, 9, 16, 25]
この並列処理対象のメソッドをpdbでデバッグしたい時、あると思います。
その際にpdbを使おうと思うと、次のように書いてしまうと思います。
test_ng_pdb.py
from multiprocessing import Pool
import pdb #追加
def f(x):
pdb.set_trace() #追加
return x*x
if __name__ == '__main__':
p = Pool(5) #数字分プロセス数を確保
print(p.map(f, [1, 2, 3, 4, 5]))
しかし、次のように実行してもエラーが出てしまい、pdbによるデバッグができません。
$ python3 test_ng_pdb.py
(略)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "test_ng_pdb.py", line 10, in <module>
print(p.map(f, [1, 2, 3, 4, 5]))
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/pool.py", line 268, in map
return self._map_async(func, iterable, mapstar, chunksize).get()
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/pool.py", line 657, in get
raise self._value
bdb.BdbQuit
先ほどの追加部分を書かずに次のように実行したとしても、同様の問題でpdbが使えません。
$ python3 -m pdb test.py
前置きが長くなりましたが、この問題を解決してpdbでデバッグしたい、という内容になります。
解決方法
stack overflowのこの記事記載の回答を試したら上手くできました、というただの検証の記事です。。
次のように、forkした子プロセス用pdbクラスを別途定義して使います。追加でsysをインポートする必要がありますが、標準ライブラリのみで済むのがとても良いなと思っています。
test_pdb_rev.py
from multiprocessing import Pool
import sys
import pdb
#下記のclassを追記
class ForkedPdb(pdb.Pdb):
"""A Pdb subclass that may be used
from a forked multiprocessing child
"""
def interaction(self, *args, **kwargs):
_stdin = sys.stdin
try:
sys.stdin = open('/dev/stdin')
pdb.Pdb.interaction(self, *args, **kwargs)
finally:
sys.stdin = _stdin
def f(x):
ForkedPdb().set_trace() #書き換え
return x*x
if __name__ == '__main__':
p = Pool(5) #数字分プロセス数を確保
print(p.map(f, [1, 2, 3, 4, 5]))
このような感じで、pdbでデバッグできます。
$ python3 test_pdb_rev.py
> /Users/purplejp/Documents/python/test_pdb_rev.py(20)f()
-> return x*x
(Pdb) > /Users/purplejp/Documents/python/test_pdb_rev.py(20)f()
-> return x*x
> /Users/purplejp/Documents/python/test_pdb_rev.py(20)f()
-> return x*x
(Pdb) (Pdb) > /Users/purplejp/Documents/python/test_pdb_rev.py(20)f()
-> return x*x
(Pdb) > /Users/purplejp/Documents/python/test_pdb_rev.py(20)f()
-> return x*x
(Pdb) x
1
普通にnを押して進むと、別のプロセスの変数も確認することもできました。
ということで、問題なくpdbを子プロセス上でも使うことができました。
検証環境
python 3.7.3
参考文献
Python公式ドキュメント「multiprocessing --- プロセスベースの並列処理」
Stack Overflow「How to attach debugger to a python subproccess?」