LoginSignup
4
3

More than 3 years have passed since last update.

python3 multiprocessingライブラリによる子プロセス処理内でも、pdbでデバッグする方法

Last updated at Posted at 2019-06-26

問題

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?」

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3