starmap set_start_method parallelize how from python debugging subprocess multiprocessing

set_start_method - ¿Cómo adjuntar el depurador a un subproceso python?



python pool process (6)

Necesito depurar un proceso hijo generado por multiprocessing.Process() . El pdb pdb parece no ser consciente de forking y no puede adjuntarse a procesos ya en ejecución.

¿Hay algún depurador de Python más inteligente que pueda asociarse a un subproceso?


Esta es una elaboración de la respuesta de Romuald que restaura la entrada original usando su descriptor de archivo. Esto mantiene la línea de lectura trabajando dentro del depurador. Además, la gestión especial de pdb de KeyboardInterrupt está deshabilitada, para no interferir con el manipulador sigint de multiprocesamiento.

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


He estado buscando una solución simple para este problema y se me ocurrió esto:

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

Úsalo de la misma manera que podrías usar el Pdb clásico:

ForkedPdb().set_trace()



Sobre la base de la idea de @memplex, tuve que modificarla para que funcionara con joblib estableciendo sys.stdin en el constructor, así como pasándolo directamente a través de joblib.

<!-- language: lang-py --> 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))


Una idea que tuve fue crear clases "ficticias" para falsificar la implementación de los métodos que está utilizando desde el multiprocesamiento:

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


Winpdb es prácticamente la definición de un depurador de Python más inteligente. Es explícitamente compatible con bajar un fork , no estoy seguro de que funcione bien con multiprocessing.Process() pero vale la pena intentarlo.

Para obtener una lista de candidatos para verificar el soporte de su caso de uso, consulte la lista de Depuradores de Python en la wiki.