web-dev-qa-db-ja.com

デバッガーをpythonサブプロセスにアタッチする方法は?

multiprocessing.Process()によって生成された子プロセスをデバッグする必要があります。 pdb deguggerはフォークを認識しておらず、すでに実行中のプロセスに接続できないようです。

サブプロセスにアタッチできるよりスマートなpythonデバッガーはありますか?

33
grep

Winpdb は、よりスマートなPythonデバッガーの定義です。明示的にサポートしています フォークを降りるmultiprocessing.Process()でうまく機能するかどうかはわかりませんしかし、試してみる価値はあります。

ユースケースのサポートを確認する候補のリストについては、wikiの Python Debuggers のリストを参照してください。

12
TryPyPy

私はこの問題の簡単な解決策を探していて、これを思いついた:

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()
52
Romuald Brunet

これは、ファイル記述子を使用して元の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
6
memeplex

@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))
1
brian.keng

私が持っていたアイデアは、マルチプロセッシングから使用しているメソッドの実装を偽造するための「ダミー」クラスを作成することでした。

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()
0
atm

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。

0
Isaiah Norton