multiprocessing.Process()
によって生成された子プロセスをデバッグする必要があります。 pdb
deguggerはフォークを認識しておらず、すでに実行中のプロセスに接続できないようです。
サブプロセスにアタッチできるよりスマートなpythonデバッガーはありますか?
Winpdb は、よりスマートなPythonデバッガーの定義です。明示的にサポートしています フォークを降りる 、multiprocessing.Process()
でうまく機能するかどうかはわかりませんしかし、試してみる価値はあります。
ユースケースのサポートを確認する候補のリストについては、wikiの Python Debuggers のリストを参照してください。
私はこの問題の簡単な解決策を探していて、これを思いついた:
import sys
import pdb
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
従来のPdbを使用するのと同じ方法で使用します。
ForkedPdb().set_trace()
これは、ファイル記述子を使用して元のstdinを復元するRomualdの回答の詳細です。これにより、readlineがデバッガー内で機能し続けます。さらに、KeyboardInterruptのpdb特別管理は、マルチプロセッシングsigintハンドラーに干渉しないように無効になっています。
class ForkablePdb(pdb.Pdb):
_original_stdin_fd = sys.stdin.fileno()
_original_stdin = None
def __init__(self):
pdb.Pdb.__init__(self, nosigint=True)
def _cmdloop(self):
current_stdin = sys.stdin
try:
if not self._original_stdin:
self._original_stdin = os.fdopen(self._original_stdin_fd)
sys.stdin = self._original_stdin
self.cmdloop()
finally:
sys.stdin = current_stdin
@memplexのアイデアに基づいて、コンストラクターでsys.stdin
を設定し、joblibを介して直接渡すことにより、joblib
で機能するように変更する必要がありました。
import os
import pdb
import signal
import sys
import joblib
_original_stdin_fd = None
class ForkablePdb(pdb.Pdb):
_original_stdin = None
_original_pid = os.getpid()
def __init__(self):
pdb.Pdb.__init__(self)
if self._original_pid != os.getpid():
if _original_stdin_fd is None:
raise Exception("Must set ForkablePdb._original_stdin_fd to stdin fileno")
self.current_stdin = sys.stdin
if not self._original_stdin:
self._original_stdin = os.fdopen(_original_stdin_fd)
sys.stdin = self._original_stdin
def _cmdloop(self):
try:
self.cmdloop()
finally:
sys.stdin = self.current_stdin
def handle_pdb(sig, frame):
ForkablePdb().set_trace(frame)
def test(i, fileno):
global _original_stdin_fd
_original_stdin_fd = fileno
while True:
pass
if __name__ == '__main__':
print "PID: %d" % os.getpid()
signal.signal(signal.SIGUSR2, handle_pdb)
ForkablePdb().set_trace()
fileno = sys.stdin.fileno()
joblib.Parallel(n_jobs=2)(joblib.delayed(test)(i, fileno) for i in range(10))
私が持っていたアイデアは、マルチプロセッシングから使用しているメソッドの実装を偽造するための「ダミー」クラスを作成することでした。
from multiprocessing import Pool
class DummyPool():
@staticmethod
def apply_async(func, args, kwds):
return DummyApplyResult(func(*args, **kwds))
def close(self): pass
def join(self): pass
class DummyApplyResult():
def __init__(self, result):
self.result = result
def get(self):
return self.result
def foo(a, b, switch):
# set trace when DummyPool is used
# import ipdb; ipdb.set_trace()
if switch:
return b - a
else:
return a - b
if __name__ == '__main__':
xml = etree.parse('C:/Users/anmendoza/Downloads/jim - 8.1/running-config.xml')
pool = DummyPool() # switch between Pool() and DummyPool() here
results = []
results.append(pool.apply_async(foo, args=(1, 100), kwds={'switch': True}))
pool.close()
pool.join()
results[0].get()
remote-pdb サブプロセスのデバッグに使用できます。インストール後、デバッグする必要のあるコードに次の行を入力します。
_import remote_pdb
remote_pdb.set_trace()
_
remote-pdbは、その特定のプロセスをデバッグするためのTelnet接続を受け入れるポート番号を出力します。ワーカーの起動順序には、さまざまなフロントエンドを使用するときにstdoutが使用される場合など、いくつかの注意事項があります。特定のポートが使用されるようにするには(無料で、現在のユーザーがアクセスできる必要があります)、代わりに次を使用します。
_from remote_pdb import RemotePdb
RemotePdb('127.0.0.1', 4444).set_trace()
_
remote-pdbを起動することもできます breakpoint()
コマンドを介して in Python 3.7。