Depuración paso a paso con IPython

170

Por lo que he leído, hay dos formas de depurar código en Python:

  • Con un depurador tradicional como pdbo ipdb. Esto apoya comandos como cpara continue, npara step-over, spara step-into, etc.), pero que no tienen acceso directo a una cáscara IPython que puede ser extremadamente útil para la inspección de objetos.

  • Usando IPython mediante la incorporación de una cáscara IPython en el código. Puede hacerlo from ipython import embedy luego usarlo embed()en su código. Cuando su programa / script llega a una embed()declaración, se lo deja caer en un shell IPython. Esto permite la inspección completa de los objetos y la prueba del código de Python utilizando todas las ventajas de IPython. Sin embargo, al usarlo embed()no puedes paso a paso a por el código con los atajos de teclado útiles.

¿Hay alguna forma de combinar lo mejor de ambos mundos? Es decir

  1. Ser capaz de paso a paso a través de su código con práctico AP / atajos de teclado IPDB.
  2. En cualquiera de estos pasos (por ejemplo, en una declaración dada), tenga acceso a un shell de IPython completo .

Depuración de IPython como en MATLAB:

Se puede encontrar un ejemplo de este tipo de "depuración mejorada" en MATLAB, donde el usuario siempre tiene acceso completo al motor / shell de MATLAB, y aún puede hacerlo paso a paso través de su código, definir puntos de interrupción condicionales, etc. Lo que he discutido con otros usuarios, esta es la función de depuración que la gente más echa de menos al pasar de MATLAB a IPython.

Depuración de IPython en Emacs y otros editores:

No quiero hacer la pregunta demasiado específica, pero trabajo principalmente en Emacs, así que me pregunto si hay alguna forma de incorporar esta funcionalidad. Idealmente , Emacs (o el editor) permitiría al programador establecer puntos de interrupción en cualquier parte del código y comunicarse con el intérprete o depurador para que se detenga en la ubicación de su elección y llevar a un intérprete completo de IPython en esa ubicación.

Amelio Vazquez-Reina
fuente
pdb tiene un !comando que ejecuta cualquier comando de python en el punto de interrupción
Dmitry Galchinsky
55
¡También estoy buscando un depurador de python similar a Matlab! Por ejemplo, hago muchos prototipos en Python Shell. Todas las variables se guardan con el shell. Ahora me encuentro con un problema. Espero depurar un pequeño fragmento de código, con esas variables calculadas con el shell. Sin embargo, un nuevo depurador no puede acceder a las variables antiguas. No es conveniente para la creación de prototipos.
user1914692
1
Para los usuarios de Emacs, RealGUD tiene una interfaz increíblemente buena.
Clément
1
Gracias @ Clément. He estado siguiendo el repositorio durante el último mes y estoy muy entusiasmado con el proyecto :) Todavía no lo he probado, pero una vez que lo haga (o si lo haces) no dudes en escribir una respuesta aquí que tal vez muestra cómo lograr lo que se solicita. Para otros como referencia, la URL es github.com/rocky/emacs-dbgr
Amelio Vazquez-Reina
@ Clément Además, si tiene alguna experiencia con RealGUD e ipdb, intenté usarlo como se explica aquí github.com/rocky/emacs-dbgr/issues/96 sin suerte.
Amelio Vazquez-Reina

Respuestas:

61

Puedes usar la %pdbmagia de IPython . Simplemente llame %pdba IPython y cuando se produzca un error, se lo colocará automáticamente ipdb. Si bien no tienes el paso de inmediato, estás enipdb después.

Esto facilita la depuración de funciones individuales, ya que puede cargar un archivo %loady luego ejecutar una función. Podría forzar un error con unassert en la posición correcta.

%pdbEs una línea mágica. Llamar como %pdb on, %pdb 1, %pdb offo %pdb 0. Si se llama sin argumento, funciona como una palanca.

sebastian
fuente
66
^ Esta es la respuesta real
César
¿Hay algo similar donde pdb tiene una funcionalidad similar a IPython sin estar inicialmente en IPython?
Lucas
44
También puede iniciar el programa con ipython --pdb file.py -- argsy se deja caer a ipdb ante una excepción. Podría valer la pena agregar a la respuesta.
Sebastian
110

¿Qué pasa con ipdb.set_trace ()? En su código:

import ipdb; ipdb.set_trace()

Actualización : ahora en Python 3.7, podemos escribir breakpoint(). Funciona igual, pero también obedece a la PYTHONBREAKPOINTvariable de entorno. Esta característica proviene de este PEP .

Esto permite una inspección completa de su código, y tiene acceso a comandos como c(continuar), n(ejecutar la siguiente línea), s(ingresar al método en el punto) y así sucesivamente.

Consulte el repositorio de ipdb y una lista de comandos . IPython ahora se llama (editar: parte de) Jupyter .


ps: tenga en cuenta que un comando ipdb tiene prioridad sobre el código python. Entonces, para escribir list(foo), necesitarías print list(foo).

Además, si le gusta la solicitud de ipython (sus modos emacs y vim, historial, finalizaciones, ...) es fácil obtener lo mismo para su proyecto, ya que se basa en el kit de herramientas de solicitud de Python .

Ehvince
fuente
10
Esta es la manera de hacerlo.
j08lue
Mi respuesta ahora incluye cómo usar ipdben Emacs usando RealGUDyisend-mode hacer exactamente lo que pide el OP.
Amelio Vazquez-Reina
1
Jupyter no es un reemplazo para IPython, sino para IPython Notebook. Los cuadernos Jupyter usan kernel en segundo plano. Para la computadora portátil Python, el núcleo suele ser un núcleo IPython. El proyecto IPython continúa más allá.
FA
1
breakpoint()es maravilloso. En PyCharm incluso te deja caer en el depurador de PyCharm. También es una forma rápida de ingresar al depurador PyCharm desde las funciones que se pegaron en la consola.
Richard Möhn
40

(Actualización el 28 de mayo de 2016) Uso de RealGUD en Emacs

Para cualquier persona en Emacs, este hilo muestra cómo lograr todo lo descrito en el OP (y más) usando

  1. un nuevo depurador importante en Emacs llamado RealGUD que puede funcionar con cualquier depurador (incluido ipdb).
  2. El paquete Emacs isend-mode.

La combinación de estos dos paquetes es extremadamente poderosa y permite recrear exactamente el comportamiento descrito en el OP y hacer aún más.

Más información sobre el artículo wiki de RealGUD para ipdb.


Respuesta original:

Después de haber probado muchos métodos diferentes para depurar Python, incluido todo lo mencionado en este hilo, una de mis formas preferidas de depurar Python con IPython es con shells incrustados.

Definición de un shell IPython incrustado personalizado:

Agregue lo siguiente en un script a su PYTHONPATH, para que el método ipsh()esté disponible.

import inspect

# First import the embed function
from IPython.terminal.embed import InteractiveShellEmbed
from IPython.config.loader import Config

# Configure the prompt so that I know I am in a nested (embedded) shell
cfg = Config()
prompt_config = cfg.PromptManager
prompt_config.in_template = 'N.In <\\#>: '
prompt_config.in2_template = '   .\\D.: '
prompt_config.out_template = 'N.Out<\\#>: '

# Messages displayed when I drop into and exit the shell.
banner_msg = ("\n**Nested Interpreter:\n"
"Hit Ctrl-D to exit interpreter and continue program.\n"
"Note that if you use %kill_embedded, you can fully deactivate\n"
"This embedded instance so it will never turn on again")   
exit_msg = '**Leaving Nested interpreter'

# Wrap it in a function that gives me more context:
def ipsh():
    ipshell = InteractiveShellEmbed(config=cfg, banner1=banner_msg, exit_msg=exit_msg)

    frame = inspect.currentframe().f_back
    msg   = 'Stopped at {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)

    # Go back one level! 
    # This is needed because the call to ipshell is inside the function ipsh()
    ipshell(msg,stack_depth=2)

Luego, cada vez que quiero depurar algo en mi código, lo coloco ipsh()justo en el lugar donde necesito hacer una inspección de objetos, etc. Por ejemplo, digamos que quiero depurarmy_function continuación

Utilizándolo:

def my_function(b):
  a = b
  ipsh() # <- This will embed a full-fledged IPython interpreter
  a = 4

y luego invoco my_function(2)de una de las siguientes maneras:

  1. Ya sea ejecutando un programa Python que invoca esta función desde un shell de Unix
  2. O invocandolo directamente desde IPython

Independientemente de cómo lo invoque, el intérprete se detiene en la línea que dice ipsh(). Una vez que haya terminado, puede hacerlo Ctrl-Dy Python reanudará la ejecución (con cualquier actualización variable que haya realizado). Tenga en cuenta que, si ejecuta el código desde un IPython normal, el shell de IPython (caso 2 anterior), el nuevo shell de IPython estará anidado dentro del que lo invocó, lo cual está perfectamente bien, pero es bueno tenerlo en cuenta. De todos modos, una vez que el intérprete se detiene en la ubicación de ipsh, puedo inspeccionar el valor de a(que puede ser2 ), ver qué funciones y objetos están definidos, etc.

El problema:

La solución anterior se puede usar para que Python se detenga en cualquier lugar que desee en su código y luego lo deje en un intérprete de IPython completo. Desafortunadamente, no le permite agregar o eliminar puntos de interrupción una vez que invoca el script, lo cual es muy frustrante. En mi opinión, este es el único que impide que IPython se convierta en una gran herramienta de depuración para Python.

Lo mejor que puedes hacer por ahora:

Una solución alternativa es colocar ipsh()a priori en las diferentes ubicaciones donde desea que el intérprete de Python inicie un shell de IPython (es decir, a breakpoint). Luego puede "saltar" entre diferentes "puntos de interrupción" predefinidos y codificados Ctrl-D, lo que saldría del shell IPython incrustado actual y se detendría nuevamente cada vez que el intérprete llegue a la siguiente llamada paraipsh() .

Si sigue esta ruta, una forma de salir del "modo de depuración" e ignorar todos los puntos de interrupción subsiguientes es utilizar lo ipshell.dummy_mode = Trueque hará que Python ignore cualquier instanciación posterior del ipshellobjeto que creamos anteriormente.

Amelio Vazquez-Reina
fuente
2
Actualice esto siempre que encuentre una solución aún mejor. Pero esto se ve genial. Lo usaré
Phani
1
¿Dónde / cómo define / importa cfg, banner_msg, exit_msg e inspecciona?
Gordon Bean
1
Esto parece funcionar bien para mí: github.com/kdodia/snippets/blob/master/ipsh.py
karan.dodia
1
Necesitaba agregar import inspectantes de que funcionara. Sin embargo, la personalización de la línea de comandos parece estar rota para mí.
Pascal
77
¿Por qué no solo usar import ipdb; ipdb.set_trace()? Tal vez extraño algo, pero no veo la ventaja de su método mucho más complicado.
luator
19

Puede iniciar la sesión de IPython desde pudb y volver a la sesión de depuración como desee.

Por cierto, ipdb está usando IPython detrás de escena y en realidad puedes usar la funcionalidad de IPython como la finalización de TAB y los comandos mágicos (el que comienza con %). Si está de acuerdo con ipdb, puede iniciarlo desde IPython usando comandos como %runy%debug . la sesión de ipdb es realmente mejor que la simple IPython en el sentido de que puede subir y bajar en el seguimiento de la pila, etc. ¿Qué falta en ipdb para "inspección de objetos"?

Además, python.el incluido con Emacs> = 24.3 tiene un buen soporte para ipdb.

tkf
fuente
1
Gracias tkf. Soy un gran admirador de su paquete Emacs-Jedi. Cuando dijo que Emacs 24.3 tiene un buen soporte para ipdb, ¿le importaría elaborar? Por lo general, inicio IPython en un M-x ansi-termbúfer separado y luego uso el modo isend para vincular mis búferes de origen al búfer de IPython para poder enviar código al intérprete de IPython con un atajo de teclado que envía automáticamente la %pastemagia al búfer de IPython. Esto me permite probar rápidamente regiones en IPython. Siempre ejecuto mis programas desde este shell de IPython con runy uso embed()para detener.
Amelio Vazquez-Reina
3
Por ejemplo, cuando recorre el código, el código fuente se abre en el otro búfer con una flecha que señala el punto de ejecución actual. También tiene el comando send-region, como lo hace con isend-mode.
tkf
Gracias @tkf ¿Cómo puedo iniciar una ipdbsesión de depuración usando python.ely tener cosas como send-regionetc. en el shell correspondiente? En general, ¿dónde puedo encontrar más información sobre esto?
Amelio Vazquez-Reina
Yo uso %runo %debug. Supongo que import ipdb; ipdb.set_trace()también funciona. No creo que pueda enviar varias líneas a ipdb. Esa es la limitación de ipdb. Pero probablemente el %pastetruco funcione. Es posible que desee enviar una solicitud de función al desarrollador de IPython.
tkf
13

Parece que el enfoque en la respuesta de @ gaborous está en desuso .

El nuevo enfoque parece ser:

from IPython.core import debugger
debug = debugger.Pdb().set_trace

def buggy_method():
    debug()
Elliot Larson
fuente
¿Esto le permite usar ipdbdeclaraciones dentro de ipython shell, mientras se depura?
alpha_989
6

Prefijar un "!" El símbolo de los comandos que escribe en pdb parece tener el mismo efecto que hacer algo en un shell de IPython. Esto funciona para acceder a la ayuda para una determinada función, o incluso nombres de variables. Quizás esto te ayude hasta cierto punto. Por ejemplo,

ipdb> help(numpy.transpose)
*** No help on (numpy.transpose)

Pero! Help (numpy.transpose) le dará la página de ayuda esperada en numpy.transpose. De manera similar para los nombres de variables, supongamos que tiene una variable l, al escribir "l" en pdb se enumera el código, pero! L imprime el valor de l.

usuario1953384
fuente
2
Este es un pdb gotcha desagradable, y vale la pena conocerlo.
Wilfred Hughes
4

¿Intentaste este consejo ?

O mejor aún, use ipython y llame a:

from IPython.Debugger import Tracer; debug_here = Tracer()

entonces puedes usar

debug_here()

cada vez que quiera establecer un punto de interrupción

gaborous
fuente
4

Puedes comenzar IPython desde dentro ipdb !

Induzca al ipdbdepurador 1 :

import idpb; ipdb.set_trace()

Ingrese IPython desde dentro en la ipdb>consola 2 :

from IPython import embed; embed()

Regrese a la ipdb>consola desde adentro IPython:

exit

Si tienes la suerte de estar usando Emacs, ¡las cosas pueden hacerse aún más cómodas!

Esto requiere usar M-x shell. Usando yasnippety bm , defina el siguiente fragmento. Esto reemplazará el texto ipdben el editor con la set-tracelínea. Después de insertar el fragmento, la línea se resaltará para que sea fácilmente perceptible y navegable. Use M-x bm-nextpara navegar.

# -*- mode: snippet -*-
# name: ipdb
# key: ipdb
# expand-env: ((yas-after-exit-snippet-hook #'bm-toggle))
# --
import ipdb; ipdb.set_trace()

1 Todo en una línea para borrar fácilmente. Como importssolo ocurre una vez, este formulario garantiza ipdbque se importará cuando lo necesite sin gastos adicionales.

2 Puede ahorrarse algo de texto importando IPython dentro de su .pdbrcarchivo :

try:
    from IPython import embed
except:
    pass

Esto le permite simplemente llamar embed()desde dentro ipdb(por supuesto, solo cuando IPython está instalado).

Lorem Ipsum
fuente
3

Una opción es utilizar un IDE como Spyder que debería permitirle interactuar con su código mientras se depura (de hecho, utilizando una consola IPython). De hecho, Spyder es muy parecido a MATLAB, lo que supongo que fue intencional. Eso incluye inspectores variables, edición variable, acceso incorporado a la documentación, etc.

Benjamin B.
fuente
3

la respuesta correcta, fácil, genial y exacta para la pregunta es usar% run macro con la marca -d.

In [4]: run -d myscript.py
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.        
> /cygdrive/c/Users/mycodefolder/myscript.py(4)<module>()
      2                                                            
      3                        
----> 4 a=1                                            
      5 b=2
Silbido
fuente
Esto debería estar más arriba. Si necesito insertar una línea en mi código (dentro de un módulo) necesito reiniciar IPython para volver a cargar el módulo. Con esto, puedo simplemente %save /tmp/foo.py x-yEl código que quiero ejecutar y depurar y luego %run -d /tmp/foo.pyestablecer el punto de interrupción donde quiera. ¡Gran respuesta!
Romeo Valentin
2

Si escribe exit () en la consola embed (), el código continuará y pasará a la siguiente línea embed ().

TurínTurambar
fuente
2

El pyzo IDE tiene capacidades similares como el PO pidió. No tiene que comenzar en modo de depuración. De manera similar a MATLAB, los comandos se ejecutan en el shell. Cuando configura un punto de interrupción en alguna línea de código fuente, el IDE detiene la ejecución allí y también puede depurar y emitir comandos regulares de IPython.

Sin embargo, parece que entrar no funciona bien (¿todavía?) (Es decir, detenerse en una línea y luego pasar a otra función) a menos que configure otro punto de interrupción.

Aún así, viniendo de MATLAB, esta parece ser la mejor solución que he encontrado.

usuario6715080
fuente
2

Desde python 3.2, tiene el interactcomando, que le da acceso al espacio completo de comandos de python / ipython.

alpha_989
fuente
1

Ejecutar desde dentro de IPython-shell de Emacs y el conjunto de puntos de interrupción a través de pdb.set_trace () debería funcionar.

Comprobado con python-mode.el, Mx ipython RET, etc.

Andreas Röhler
fuente
1

Desarrollando nuevo código

Depuración dentro de IPython

  1. Utilice la ejecución de la celda Jupyter / IPython para acelerar las iteraciones del experimento
  2. Use %% debug para pasar

Ejemplo de celda:

%%debug
...: for n in range(4):
...:    n>2

Depuración de código existente

IPython dentro de la depuración

  1. Depuración de una prueba unitaria rota: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. Depuración fuera del caso de prueba: breakpoint(), python -m ipdb, etc.
  3. IPython.embed () para la funcionalidad completa de IPython donde sea necesario mientras está en el depurador

Pensamientos sobre Python

Estoy de acuerdo con el OP en que muchas cosas que MATLAB hace muy bien que Python aún no tiene y realmente debería, ya que casi todo en el lenguaje favorece la velocidad de desarrollo sobre la velocidad de producción. Quizás algún día contribuya más que triviales correcciones de errores a CPython.

https://github.com/ipython/ipython/commit/f042f3fea7560afcb518a1940daa46a72fbcfa68

Consulte también ¿Es posible ejecutar comandos en IPython con depuración?

SwimBikeRun
fuente