web-dev-qa-db-fra.com

Débogage étape par étape avec IPython

D'après ce que j'ai lu, il existe deux manières de déboguer du code en Python:

  • Avec un débogueur traditionnel tel que pdb ou ipdb. Ceci supporte des commandes telles que c pour continue, n pour step-over, s pour step-into etc.), mais vous n'avez pas accès direct à un shell IPython, ce qui peut s'avérer extrêmement utile pour l'inspection d'objets.

  • Utilisation de IPython par incorporation un shell IPython dans votre code. Vous pouvez faire from ipython import embed, puis utiliser embed() dans votre code. Lorsque votre programme/script atteint une instruction embed(), vous êtes déposé dans un shell IPython. Cela permet l'inspection complète des objets et le test du code Python à l'aide de tous les goodies IPython. Cependant, lorsque vous utilisez embed(), vous ne pouvez plus pas à pas utiliser le code avec des raccourcis clavier pratiques.

Est-il possible de combiner le meilleur des deux mondes? C'est à dire.

  1. Soyez capable de étape par étape grâce à votre code avec les raccourcis clavier pdb/ipdb pratiques.
  2. À n'importe quelle étape (par exemple, sur une instruction donnée), accédez à un shell IPython à part entière .

Débogage IPython comme dans MATLAB:

Un exemple de ce type de "débogage amélioré" peut être trouvé dans MATLAB, où l'utilisateur a toujours un accès complet au moteur/shell MATLAB, et elle peut toujours étape par étape grâce à son code, définir des points d'arrêt conditionnels, etc. De ce que j'ai discuté avec d'autres utilisateurs, il s'agit de la fonctionnalité de débogage ce sont les personnes qui manquent le plus lorsqu’on passe de MATLAB à IPython.

Débogage IPython dans Emacs et d'autres éditeurs:

Je ne veux pas rendre la question trop spécifique, mais je travaille principalement dans Emacs, alors je me demande s’il existe un moyen d’y apporter cette fonctionnalité. Idéalement , Emacs (ou l'éditeur) permettrait au programmeur de définir des points d'arrêt n'importe où dans le code et de communiquer avec l'interpréteur ou le débogueur pour qu'il s'arrête l’emplacement de votre choix et invitez un interprète IPython complet sur cet emplacement.

155

Personne n'a encore mentionné le drapeau %pdb d'IPython. Appelez simplement %pdb dans IPython et lorsqu'une erreur se produit, vous êtes automatiquement remplacé par ipdb. Bien que le pas ne soit pas immédiat, vous êtes dans ipdb après.

Cela facilite le débogage de fonctions individuelles, vous pouvez simplement charger un fichier avec %load puis exécuter une fonction. Vous pourriez forcer une erreur avec un assert à la bonne position.

40
sebastian

Qu'en est-il de ipdb.set_trace ()? Dans votre code:

import ipdb; ipdb.set_trace()

update : maintenant dans Python 3.7, nous pouvons écrire breakpoint(). Cela fonctionne de la même manière, mais il obéit également à la variable d’environnement PYTHONBREAKPOINT. Cette fonctionnalité provient de this PEP .

Cela permet une inspection complète de votre code et vous avez accès à des commandes telles que c (continuer), n (exécuter la ligne suivante), s (entrer dans la méthode au point) etc.

Voir les ipdb repo et ne liste de commandes . IPython s'appelle maintenant (edit: part of) Jupyter .


ps: remarque qu'une commande ipdb a la priorité sur le code python. Donc, pour écrire list(foo), vous aurez besoin de print list(foo).

De plus, si vous aimez ipython Prompt (ses modes emacs et vim, son historique, ses finitions,…), il est facile d’obtenir la même chose pour votre projet car elle est basée sur boîte à outils python Prompt .

103
Ehvince

(Mise à jour du 28 mai 2016) Utilisation de RealGUD dans Emacs

Pour tout le monde dans Emacs, ce fil montre comment accomplir tout ce qui est décrit dans le PO (et plus) en utilisant

  1. un nouveau débogueur important dans Emacs appelé RealGUD qui peut fonctionner avec n’importe quel débogueur (y compris ipdb).
  2. Le paquet Emacs isend-mode.

La combinaison de ces deux packages est extrêmement puissante et permet de recréer exactement le comportement décrit dans l’OP et d’en faire encore plus.

Plus d'informations sur l'article du wiki de RealGUD pour ipdb.


Réponse originale:

Après avoir essayé de nombreuses méthodes différentes pour déboguer Python, y compris tout ce qui est mentionné dans ce fil de discussion, l’un de mes moyens préférés de déboguer Python avec IPython consiste à utiliser des shells intégrés.

Définition d'un shell IPython intégré personnalisé:

Ajoutez ce qui suit sur un script à votre PYTHONPATH, afin que la méthode ipsh() devienne 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)

Ensuite, chaque fois que je veux déboguer quelque chose dans mon code, je place ipsh() juste à l'emplacement où j'ai besoin d'inspecter un objet, etc. Par exemple, disons que je veux déboguer my_function ci-dessous

En l'utilisant:

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

puis j'invoque my_function(2) de l'une des manières suivantes:

  1. Soit en exécutant un programme Python qui appelle cette fonction à partir d'un shell Unix
  2. Ou en l'invoquant directement depuis IPython

Indépendamment de la façon dont je l’appelle, l’interprète s’arrête à la ligne qui indique ipsh(). Une fois que vous avez terminé, vous pouvez faire Ctrl-D et Python reprendre l'exécution (avec toutes les mises à jour variables que vous avez effectuées). Notez que, si vous exécutez le code à partir d’un IPython normal, le shell IPython (cas 2 ci-dessus), le nouveau shell IPython sera imbriqué dans celui de que vous avez invoqué, ce qui est parfaitement correct, mais il est bon d’en être conscient. Quoi qu'il en soit, une fois que l'interprète s'arrête à l'emplacement de ipsh, je peux inspecter la valeur de a (qui est 2), voir quelles fonctions et quels objets sont définis, etc.

Le problème:

La solution ci-dessus peut être utilisée pour que Python s'arrête où vous le souhaitez dans votre code, puis vous place dans un interpréteur IPython à part entière. Malheureusement, cela ne vous permet pas d'ajouter ou de supprimer des points d'arrêt une fois le script invoqué, ce qui est très frustrant. À mon avis, c'est la chose seulement qui empêche IPython de devenir un excellent outil de débogage pour Python.

Le mieux que vous puissiez faire pour l'instant:

Une solution de contournement consiste à placer ipsh() a priori aux différents emplacements où vous voulez que l'interpréteur Python lance un shell IPython (c'est-à-dire un breakpoint). Vous pouvez ensuite "sauter" entre différents "points d'arrêt" prédéfinis et codés en dur avec Ctrl-D, ce qui permet de quitter le shell IPython intégré actuel et de s'arrêter à chaque fois que l'interpréteur répond au prochain appel à ipsh().

Si vous suivez cette route, une façon de quitter le "mode débogage" et d'ignorer tous les points d'arrêt ultérieurs consiste à utiliser ipshell.dummy_mode = True, ce qui fera en sorte que Python ignore toutes les instanciations ultérieures de l'objet ipshell que nous avons créé ci-dessus.

40

Vous pouvez démarrer la session IPython à partir de pudb et revenir à la session de débogage à votre guise.

BTW, ipdb utilise IPython dans les coulisses et vous pouvez utiliser les fonctionnalités IPython telles que l’achèvement de la tabulation et les commandes magiques (celle-ci commence par %). Si vous êtes d'accord avec ipdb, vous pouvez le lancer depuis IPython à l'aide de commandes telles que %run et %debug. La session ipdb est en fait meilleure que la simple IPython dans le sens où vous pouvez monter et descendre dans la trace de la pile, etc. Qu'est-ce qui manque dans ipdb pour "l'inspection d'objet"?

De plus, python.el fourni avec Emacs> = 24.3 prend en charge Nice ipdb.

19
tkf

On dirait que l'approche dans la réponse de @ gaborous est obsolète .

La nouvelle approche semble être:

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

def buggy_method():
    debug()
11
Elliot Larson

Préfixer un "!" Le symbole des commandes que vous tapez dans pdb semble avoir le même effet que de faire quelque chose dans un shell IPython. Cela fonctionne pour accéder à l'aide pour une certaine fonction, ou même à des noms de variables. Peut-être que cela vous aidera dans une certaine mesure. Par exemple,

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

Mais! Help (numpy.transpose) vous donnera la page d’aide attendue sur numpy.transpose. De même pour les noms de variable, disons que vous avez une variable l, taper "l" dans pdb donne le code, mais! L affiche la valeur de l.

6
user1953384

Avez-vous essayé cette astuce ?

Ou mieux encore, utilisez ipython et appelez:

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

alors vous pouvez simplement utiliser

debug_here()

chaque fois que vous voulez définir un point d'arrêt

4
gaborous

Une option consiste à utiliser un IDE comme Spyder , ce qui devrait vous permettre d'interagir avec votre code pendant le débogage (à l'aide d'une console IPython, en fait). En fait, Spyder ressemble beaucoup à MATLAB, ce qui, je suppose, était intentionnel. Cela inclut les inspecteurs de variables, l'édition de variables, l'accès intégré à la documentation, etc.

3
user2423516

la bonne réponse, facile, cool, exacte pour la question consiste à utiliser la macro% run avec le drapeau -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
3
Ping

Le Pyzo IDE a des capacités similaires à celles demandées par le PO. Vous n'êtes pas obligé de démarrer en mode débogage. À l'instar de MATLAB, les commandes sont exécutées dans le shell. Lorsque vous définissez un point d'arrêt dans une ligne de code source, le IDE arrête l'exécution et vous pouvez également déboguer et émettre des commandes IPython standard.

Cependant, il semble que cette opération ne fonctionne pas (encore?) Correctement (c’est-à-dire s’arrêter sur une ligne puis sur une autre fonction), à moins que vous ne définissiez un autre point de rupture.

Cependant, venant de MATLAB, cela semble être la meilleure solution que j'ai trouvée.

2
user6715080

Si vous tapez exit () dans la console embed (), le code continue et passe à la ligne suivante embed ().

2
TurinTurambar

Courir depuis l'intérieur de IPython-Shell d'Emacs et le point d'arrêt défini via pdb.set_trace () devraient fonctionner.

Vérifié avec python-mode.el, M-x ipython RET etc.

1
Andreas Röhler

Dans python 3.2, vous avez la commande interact, qui vous permet d'accéder à l'espace de commande complet Python/ipython.

1
alpha_989

Non explicitement dit auparavant, vous pouvez appeler IPython.embed() de l'intérieur ipdb.

ipdb est idéal pour parcourir du code et pour effectuer une inspection, mais il ne gère pas d'autres éléments, tels que plusieurs lignes, bien. IPython gère bien les instructions multilignes.

Pour induire le débogueur, appelez ce qui suit dans votre code:

import idpb; ipdb.set_trace()

Cela permet une introspection et une progression.

Si vous avez besoin de la fonctionnalité IPython, appelez ce qui suit dans ipdb>:

from IPython import embed; embed()

En ce qui concerne Emacs, voici ma solution idiosyncratique qui, je l’espère, vous inspirera.

J'utilise Emacs avec M-x Shell. J'ai un yassnippet défini pour ipdb qui induit le débogueur. Le paquet bm met en évidence la ligne afin que je n’oublie pas de la supprimer du code source:

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

J'importe IPython dans mon fichier .pdbrc:

try:
    from IPython import embed
except:
    pass

Cela me permet d’appeler embed() à partir de n’importe quelle instance de ipdb (lorsque IPython est installé).

1
Lorem Ipsum