Depuración paso a paso con IPython

12 minutos de lectura

avatar de usuario
Amelio Vázquez-Reina

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

  • Con un depurador tradicional como pdb o ipdb. Esto admite comandos como c por continue, n por step-over, s por step-into etc.), pero no tiene acceso directo a un shell IPython que puede ser extremadamente útil para la inspección de objetos.

  • Usando IPython por incrustación un shell de IPython en su código. Tu puedes hacer from IPython import embedy luego usar embed() en tu código. Cuando su programa/guión llega a un embed() instrucción, se le coloca en un shell de 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 usar embed() no puedes paso a paso a través del código con prácticos atajos de teclado.

¿Hay alguna manera 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ácticos atajos de teclado pdb/ipdb.
  2. En cualquiera de esos pasos (por ejemplo, en una declaración dada), tener acceso a un completo shell IPython.

Depuración de IPython como en MATLAB:

Un ejemplo de este tipo de “depuración mejorada” se puede encontrar en MATLAB, donde el usuario siempre tiene acceso completo al motor/shell de MATLAB, y todavía puede paso a paso a través de su código, definir puntos de interrupción condicionales, etc. Por lo que he discutido con otros usuarios, esta es la función de depuración que la gente más extraña 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. IdealmenteEmacs (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 el depurador para que se detenga en la ubicación de su elección y lo traiga a un intérprete de IPython completo en esa ubicación.

  • pdb tiene ! comando que ejecuta cualquier comando de python en el punto de interrupción

    – Dmitri Galchinsky

    1 de junio de 2013 a las 0:56

  • ¡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 una pequeña pieza 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.

    – usuario1914692

    9 de junio de 2013 a las 19:59

  • Para los usuarios de Emacs, RealGUD tiene una interfaz increíblemente buena.

    – Clemente

    28 mayo 2016 a las 16:32

  • 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 hace), siéntase libre de 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 Vázquez-Reina

    29 de mayo de 2016 a las 0:05


  • @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 Vázquez-Reina

    29 de mayo de 2016 a las 1:20

avatar de usuario
evidenciar

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

import ipdb; ipdb.set_trace()

actualizar: ahora en Python 3.7, podemos escribir breakpoint(). Funciona igual, pero también obedece a las PYTHONBREAKPOINT Variable ambiental. Esta característica viene de este PEP.

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

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


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

Además, si le gusta el indicador 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.

  • Esta es la manera de hacerlo.

    – j08lue

    15 de enero de 2015 a las 12:49

  • Mi respuesta ahora incluye cómo usar ipdb en Emacs usando RealGUD y isend-mode para hacer exactamente lo que pide el OP.

    – Amelio Vázquez-Reina

    29 de mayo de 2016 a las 2:47

  • Jupyter no reemplaza a IPython, sino a IPython Notebook. Los cuadernos de Jupyter usan kernel en segundo plano. Para el cuaderno de Python, el kernel suele ser un kernel de IPython. los Proyecto IPython continúa más.

    – AF

    14 de septiembre de 2017 a las 9:15

  • breakpoint() es maravilloso. En PyCharm, incluso te deja en el depurador de PyCharm. También es una forma rápida de ingresar al depurador de PyCharm desde las funciones que se pegaron en la consola.

    – Richard Mohn

    5 de diciembre de 2019 a las 22:07


  • Después de la actualización, simplemente podría establecer la variable de entorno. PYTHONBREAKPOINT=IPython.embed y entonces breakpoint() hará el trabajo

    – Hritik

    24 de enero a las 13:07

avatar de usuario
sebastián

Puedes usar IPython %pdb magia. Solo llama %pdb en IPython y cuando se produce un error, se le baja automáticamente a ipdb. Si bien no tiene el paso inmediato, está en ipdb después.

Esto facilita la depuración de funciones individuales, ya que solo puede cargar un archivo con %load y luego ejecutar una función. Podrías forzar un error con un assert en la posición correcta.

%pdb es una línea mágica. Llámalo como %pdb on, %pdb 1, %pdb off o %pdb 0. Si se llama sin argumentos, funciona como un conmutador.

  • ^ Esta es la verdadera respuesta

    – César

    18 de julio de 2017 a las 11:53

  • ¿Hay algo similar donde pdb tiene una funcionalidad similar a IPython sin estar inicialmente en IPython?

    – Lucas

    26/09/2017 a las 21:41

  • También puede iniciar el programa con ipython --pdb file.py -- args y se colocan en ipdb ante una excepción. Podría valer la pena agregar a la respuesta.

    – sebastián

    20 de marzo de 2019 a las 15:07

avatar de usuario
Amelio Vázquez-Reina

(Actualizado el 28 de mayo de 2016) Uso de RealGUD en Emacs

Para cualquiera 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 operar con cualquier depurador (incluyendo 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 de la 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 integrados.

Definición de un shell de IPython incrustado personalizado:

Agregue lo siguiente en un script a su PYTHONPATHde modo que el método ipsh() se vuelve 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, coloco ipsh() justo en el lugar donde necesito hacer una inspección de objetos, etc. Por ejemplo, digamos que quiero depurar my_function abajo

Usá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 de Python que invoque esta función desde un shell de Unix
  2. O invocándolo 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 hacer Ctrl-D y Python reanudará la ejecución (con las actualizaciones de variables que haya realizado). Tenga en cuenta que, si ejecuta el código desde un IPython regular, el shell de IPython (caso 2 anterior), el nuevo shell de IPython será anidado dentro del que lo invocaste, 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 ipshpuedo inspeccionar el valor de a (que ser 2), 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 coloque 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 solamente cosa 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 es colocar ipsh() a priori en las diferentes ubicaciones donde desea que el intérprete de Python inicie un shell de IPython (es decir, un breakpoint). Luego puede “saltar” entre diferentes “puntos de interrupción” predefinidos y codificados con Ctrl-Dque saldría del shell IPython incrustado actual y se detendría nuevamente cada vez que el intérprete haga la siguiente llamada a ipsh().

Si sigue esta ruta, una forma de salir del “modo de depuración” e ignorar todos los puntos de interrupción posteriores es usar ipshell.dummy_mode = True lo que hará que Python ignore cualquier instanciación posterior del ipshell objeto que creamos arriba.

  • Actualice esto cada vez que encuentre una solución aún mejor. Pero esto se ve genial. lo usaré

    – Fani

    12 de noviembre de 2014 a las 18:33

  • ¿Dónde/cómo define/importa cfg, banner_msg, exit_msg e inspecciona?

    -Gordon Bean

    20 de noviembre de 2014 a las 19:19

  • Esto parece funcionar bien para mí: github.com/kdodia/snippets/blob/master/ipsh.py

    – karan.dodia

    18 dic 2014 a las 16:40

  • necesitaba agregar import inspect antes de que funcionara. Sin embargo, la personalización de la línea de comandos parece estar rota para mí.

    – Pascual

    23 de abril de 2015 a las 9:24


  • ¿Por qué no simplemente usar import ipdb; ipdb.set_trace()? Tal vez me perdí algo, pero no veo la ventaja de su método mucho más complicado.

    – luator

    5 sep 2015 a las 14:45

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

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

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

Parece el enfoque en la respuesta de @gaborous es obsoleto.

El nuevo enfoque parece ser:

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

def buggy_method():
    debug()

  • ¿Esto te permite usar ipdb declaraciones dentro de ipython shell, durante la depuración?

    – alfa_989

    1 de enero de 2018 a las 19:48

avatar de usuario
usuario1953384

Anteponer un “!” El símbolo para 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 de una determinada función, o incluso nombres de variables. Tal vez 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, digamos que tiene una variable l, escribir “l” en pdb enumera el código, pero !l imprime el valor de l.

  • ¿Esto te permite usar ipdb declaraciones dentro de ipython shell, durante la depuración?

    – alfa_989

    1 de enero de 2018 a las 19:48

Tu puedes empezar IPython desde dentro ipdb.

Inducir el ipdb depurador1:

import idpb; ipdb.set_trace()

Introduzca IPython desde dentro en el ipdb> consola2:

from IPython import embed; embed()

Volver a la ipdb> consola desde dentro IPython:

exit

Si tiene la suerte de usar Emacs, las cosas se pueden hacer aún más convenientes.

Esto requiere usar M-x shell. Usando yasnippet y b.m., defina el siguiente fragmento. Esto reemplazará el texto. ipdb en el editor con el set-trace línea. Después de insertar el fragmento, la línea se resaltará para que se pueda ver y navegar fácilmente. Usar M-x bm-next para 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 una fácil eliminación. Ya que imports solo sucede una vez, este formulario asegura ipdb se importará cuando lo necesite sin gastos generales adicionales.

2 Puede ahorrarse algo de tipeo importando IPython dentro de tu .pdbrc expediente:

try:
    from IPython import embed
except:
    pass

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

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad