Iniciando el depurador de Python automáticamente por error

216

Esta es una pregunta que me he preguntado durante bastante tiempo, pero nunca he encontrado una solución adecuada. Si ejecuto un script y me encuentro, digamos un IndexError, python imprime la línea, la ubicación y la descripción rápida del error y las salidas. ¿Es posible iniciar pdb automáticamente cuando se encuentra un error? No estoy en contra de tener una declaración de importación adicional en la parte superior del archivo, ni algunas líneas de código adicionales.

Jeremy
fuente
12
¿Has considerado cambiar la respuesta que se acepta?
Joost

Respuestas:

127

Puede usar traceback.print_exc para imprimir las excepciones traceback. Luego use sys.exc_info para extraer el rastreo y finalmente llame a pdb.post_mortem con ese rastreo

import pdb, traceback, sys

def bombs():
    a = []
    print a[0]

if __name__ == '__main__':
    try:
        bombs()
    except:
        extype, value, tb = sys.exc_info()
        traceback.print_exc()
        pdb.post_mortem(tb)

Si desea iniciar una línea de comando interactiva con code.interact usando los locales del marco donde se originó la excepción, puede hacerlo

import traceback, sys, code

def bombs():
    a = []
    print a[0]

if __name__ == '__main__':
    try:
        bombs()
    except:
        type, value, tb = sys.exc_info()
        traceback.print_exc()
        last_frame = lambda tb=tb: last_frame(tb.tb_next) if tb.tb_next else tb
        frame = last_frame().tb_frame
        ns = dict(frame.f_globals)
        ns.update(frame.f_locals)
        code.interact(local=ns)
Florian Bösch
fuente
la primera solución se discute más en el libro de cocina de python
dirkjot
3
¿Por qué preferiría alguien codemás pdbya que este último parece expandirse sobre el primero?
K3 --- rnc
Tengo la misma pregunta? ¿Por qué preferirías code?
ARH
2
Luego use sys.exc_infopara extraer el rastreo y finalmente llame pdb.post_mortemcon ese rastreo . No necesita pasar el objeto de rastreo a pdb.post_mortem. Desde documentos : si no se proporciona un rastreo, se usa uno de la excepción que se está manejando actualmente (se debe manejar una excepción si se va a usar el valor predeterminado).
Piotr Dobrogost
2
@PiotrDobrogost Buen punto. Sin embargo, creo que es más útil saber que puede pasar un objeto tb, ya que demuestra mejor la API. Es bueno saber que existen ambas opciones.
davidA
453
python -m pdb -c continue myscript.py

Si no proporciona la -c continuebandera, deberá ingresar 'c' (para Continuar) cuando comience la ejecución. Luego se ejecutará hasta el punto de error y le dará el control allí. Como mencionó eqzx , este indicador es una nueva adición en python 3.2, por lo que se requiere ingresar 'c' para las versiones anteriores de Python (consulte https://docs.python.org/3/library/pdb.html ).

Catherine Devlin
fuente
55
Gracias por mencionar el " enter 'c' " - Normalmente solía ingresar 'r' (para "ejecutar"), acostumbrándome desde gdb; y cuando ingresa 'r' pdb, el programa se ejecuta, pero NO se detiene (ni genera retroceso) por error; me dejó perplejo hasta que leí esto. ¡Salud!
sdaau
3
Vineet, comenzará con el depurador activado, así que ingrese "cont" y se ejecutará hasta que se encuentre el error. Desde allí puede inspeccionar variables, etc., como en cualquier otra sesión pdb.
Catherine Devlin
40
OP, acepta esto como respuesta. Esto es lo más útil y he perdido 5 minutos leyendo los otros hasta llegar a este ... ¡esto debería ser lo primero!
jhegedus
44
Esto también funciona igual con ipdb; y, por supuesto, se pueden agregar argumentos después del guión!
tutuDajuju
20
Esto no funciona con Python 2.7. docs.python.org/3/library/pdb.html : "Nuevo en la versión 3.2: pdb.py ahora acepta una opción -c que ejecuta comandos"
eqzx
68

Use el siguiente módulo:

import sys

def info(type, value, tb):
    if hasattr(sys, 'ps1') or not sys.stderr.isatty():
    # we are in interactive mode or we don't have a tty-like
    # device, so we call the default hook
        sys.__excepthook__(type, value, tb)
    else:
        import traceback, pdb
        # we are NOT in interactive mode, print the exception...
        traceback.print_exception(type, value, tb)
        print
        # ...then start the debugger in post-mortem mode.
        # pdb.pm() # deprecated
        pdb.post_mortem(tb) # more "modern"

sys.excepthook = info

Póngale un nombre debug(o lo que quiera) y colóquelo en algún lugar de su ruta de Python.

Ahora, al comienzo de su script, simplemente agregue un import debug.

tzot
fuente
2
Esta debería ser la respuesta aceptada: no requiere ninguna modificación del código existente o envolver todo en una try-catchIMO fea.
cyphar
Me encanta esta respuesta con bastante frecuencia, pero prefiero pudbsobre pdb. Sigue volviendo a copiar y pegar, que seguramente dice algo sobre la falta de orden en mi vida.
Stabledog
47

Ipython tiene un comando para alternar este comportamiento: % pdb . Hace exactamente lo que describió, tal vez incluso un poco más (ofreciéndole retrocesos más informativos con resaltado de sintaxis y finalización de código). Definitivamente vale la pena intentarlo!

Latanius
fuente
3
Y esa es la única respuesta razonable a esto.
Michael
44
Tenga en cuenta que, como también se señaló en los documentos vinculados @matthiash, %debugpermite abrir el depurador después de encontrar un error. A menudo prefiero esto sobre %pdb. (La disyuntiva no es más que escribiendo qcada vez que no desea depurar un error de escritura vs. %debugcada vez que lo hace quiere depurar un error.)
Braham Snyder
1
Tenga en cuenta también que la configuración c.InteractiveShell.pdb = Trueen uno se ipython_config.pyactiva %pdbautomáticamente para cada sesión.
Braham Snyder
33

Este no es el depurador, pero probablemente igual de útil (?)

Sé que escuché a Guido mencionar esto en un discurso en alguna parte.

Acabo de comprobar python - ?, y si usas el comando -i puedes interactuar donde se detuvo tu script.

Entonces, dado este script:

testlist = [1,2,3,4,5, 0]

prev_i = None
for i in testlist:
    if not prev_i:
        prev_i = i
    else:
        result = prev_i/i

¡Puedes obtener esta salida!

PS D:\> python -i debugtest.py
Traceback (most recent call last):
  File "debugtest.py", line 10, in <module>
    result = prev_i/i
ZeroDivisionError: integer division or modulo by zero
>>>
>>>
>>> prev_i
1
>>> i
0
>>>

Para ser sincero, no he usado esto, pero debería serlo, parece muy útil.

Monkut
fuente
Ligero, pero a menudo justo lo que se necesita
Casebash
8
No tan útil, se lanza al alcance global. No se puede hurgar en la función bloqueada.
pixelpax
21

IPython simplifica esto en la línea de comando:

python myscript.py arg1 arg2

puede reescribirse a

ipython --pdb myscript.py -- arg1 arg2

O, de manera similar, si se llama a un módulo:

python -m mymodule arg1 arg2

puede reescribirse a

ipython --pdb -m mymodule -- arg1 arg2

Tenga en cuenta que --para evitar que IPython lea los argumentos del script como propios.

Esto también tiene la ventaja de invocar el depurador de IPython mejorado (ipdb) en lugar de pdb.

wodow
fuente
9

Si está utilizando ipython, después de iniciar escriba%pdb

In [1]: %pdb
Automatic pdb calling has been turned ON
Willemoes
fuente
Jupyter por ejemplo
Blaztix
6

Si está utilizando el entorno IPython, puede usar el% debug y el shell lo llevará de regreso a la línea ofensiva con el entorno ipdb para inspecciones, etc. Otra opción como se indicó anteriormente es usar el iPython magic% pdb que efectivamente lo hace lo mismo.

Jehandad
fuente
Tenga en cuenta que si el error ocurrió en una función del módulo, puede navegar a través de los marcos upy los downcomandos para volver a la línea de su código que generó el error.
Jean Paul el
4

Puedes poner esta línea en tu código:

import pdb ; pdb.set_trace()

Más información: Inicie el depurador de python en cualquier línea

Rory
fuente
8
Esto detiene el código e inicia un depurador en la línea donde coloca este comando, no en la línea donde se produjo una excepción
blueFast
3

Para que se ejecute sin tener que escribir c al principio, use:

python -m pdb -c c <script name>

Pdb tiene sus propios argumentos de línea de comando: -cc ejecutará el comando c (en curso) al inicio de la ejecución y el programa se ejecutará sin interrupciones hasta el error.

Zlatko Karakaš
fuente
3

python -m pdb script.py en python2.7 presione continuar para comenzar y se ejecutará el error y se romperá allí para la depuración.

Hannah
fuente
1

Si está ejecutando un módulo:

python -m mymodule

Y ahora desea ingresar pdbcuando se produce una excepción, haga esto:

PYTHONPATH="." python -m pdb -c c mymodule/__main__.py

(o extienda su PYTHONPATH). Se PYTHONPATHnecesita para que el módulo se encuentre en la ruta, ya que está ejecutando el pdbmódulo ahora.

blueFast
fuente
0

Coloque un punto de interrupción dentro del constructor de la clase de excepción más alta en la jerarquía, y la mayoría de las veces verá dónde se generó el error.

Poner un punto de interrupción significa lo que quieras que signifique: puedes usar un IDE, o pdb.set_trace, o lo que sea

vlad-ardelean
fuente