Débogage étape par étape avec IPython

170

D'après ce que j'ai lu, il existe deux façons de déboguer le code en Python:

  • Avec un débogueur traditionnel tel que pdbou ipdb. Cela prend en charge les commandes telles que cfor continue, nfor step-over, sfor step-intoetc.), mais vous n'avez pas d'accès direct à un shell IPython qui peut être extrêmement utile pour l'inspection d'objets.

  • Utiliser IPython en incorporant un shell IPython dans votre code. Vous pouvez le faire from ipython import embed, puis utiliser embed()dans votre code. Lorsque votre programme / script atteint une embed()instruction, vous êtes placé dans un shell IPython. Cela permet l'inspection complète des objets et le test du code Python en utilisant tous les goodies IPython. Cependant, lors de l'utilisation, embed()vous ne pouvez plus parcourir le code pas à pas avec des raccourcis clavier pratiques.

Existe-t-il un moyen de combiner le meilleur des deux mondes? C'est à dire

  1. Soyez capable de parcourir votre code pas à pas avec les raccourcis clavier pratiques pdb / ipdb.
  2. À chaque étape de ce type (par exemple sur une instruction donnée), ayez accès à 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 il peut toujours pas à pas dans son code, définir des points d'arrêt conditionnels, etc. ce que j'ai discuté avec d'autres utilisateurs, c'est la fonctionnalité de débogage qui manque le plus aux gens lors du passage de MATLAB à IPython.

Débogage IPython dans Emacs et autres éditeurs:

Je ne veux pas rendre la question trop précise, mais je travaille principalement dans Emacs, donc je me demande s'il y a un moyen d'y intégrer cette fonctionnalité. Idéalement , Emacs (ou l'éditeur) permettrait au programmeur de définir des points d'arrêt n'importe où sur 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 amener un interpréteur IPython complet à cet emplacement.

Amelio Vazquez-Reina
la source
pdb a une !commande qui exécute n'importe quelle commande python au point d'arrêt
Dmitry Galchinsky
5
Je recherche également un débogueur python similaire à Matlab! Par exemple, je fais beaucoup de prototypage en shell python. Toutes les variables sont enregistrées avec le shell. Maintenant, je rencontre un problème. J'espère déboguer un petit morceau de code, avec ces variables calculées avec le shell. Cependant, un nouveau débogueur ne peut pas accéder aux anciennes variables. Ce n'est pas pratique pour le prototypage.
user1914692
1
Pour les utilisateurs d'Emacs, RealGUD a une interface incroyablement bonne.
Clément
1
Merci @ Clément J'ai suivi le repo au cours du dernier mois et je suis très enthousiasmé par le projet :) Je ne l'ai pas encore essayé, mais une fois que je le fais (ou si vous le faites), n'hésitez pas à écrire une réponse ici. montre comment accomplir ce qui est demandé. Pour d'autres à titre de référence, l'URL est github.com/rocky/emacs-dbgr
Amelio Vazquez-Reina
@ Clément Aussi, si vous avez une expérience avec RealGUD & ipdb, j'ai essayé de l'utiliser comme expliqué ici github.com/rocky/emacs-dbgr/issues/96 sans chance.
Amelio Vazquez-Reina

Réponses:

61

Vous pouvez utiliser la %pdbmagie d'IPython . Appelez simplement %pdbIPython et lorsqu'une erreur se produit, vous êtes automatiquement redirigé vers ipdb. Bien que vous n'ayez pas le pas immédiatement, vous êtes ipdbaprès.

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

%pdbest une ligne magique. Appelez comme %pdb on, %pdb 1, %pdb offou %pdb 0. S'il est appelé sans argument, il fonctionne comme une bascule.

Sébastien
la source
6
^ C'est la vraie réponse
Cesar
Y a-t-il quelque chose de similaire où pdb a des fonctionnalités similaires à IPython sans être initialement dans IPython?
Lucas du
4
Vous pouvez également démarrer le programme avec ipython --pdb file.py -- argset être déposé sur ipdb en cas d'exception. Cela pourrait valoir la peine d’ajouter à la réponse.
sebastian
110

Et ipdb.set_trace ()? Dans votre code:

import ipdb; ipdb.set_trace()

mise à jour : maintenant en Python 3.7, nous pouvons écrire breakpoint(). Cela fonctionne de la même manière, mais il obéit également à la PYTHONBREAKPOINTvariable d'environnement. Cette fonctionnalité provient de ce 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) et ainsi de suite.

Voir le référentiel ipdb et une liste de commandes . IPython est maintenant appelé (modifier: une partie de) Jupyter .


ps: notez qu'une commande ipdb est prioritaire sur le code python. Donc, pour écrire, list(foo)vous en aurez besoin print list(foo).

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

Ehvince
la source
10
C'est la manière de procéder.
j08lue
Ma réponse comprend maintenant comment utiliser ipdbdans Emacs en utilisant RealGUDet isend-modefaire exactement ce que l'OP demande.
Amelio Vazquez-Reina
1
Jupyter ne remplace pas IPython, mais IPython Notebook. Les notebooks Jupyter utilisent le noyau en arrière-plan. Pour le notebook Python, le noyau est généralement un noyau IPython. Le projet IPython se poursuit.
FA du
1
breakpoint()est merveilleux. Dans PyCharm, il vous dépose même dans le débogueur PyCharm. C'est également un moyen rapide d'accéder au débogueur PyCharm à partir de fonctions collées dans la console.
Richard Möhn
40

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

Pour n'importe qui dans Emacs, ce fil montre comment accomplir tout ce qui est décrit dans l'OP (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 package 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 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, l'un de mes moyens préférés de déboguer Python avec IPython est d'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'endroit où je dois faire l'inspection d'objet, etc. Par exemple, disons que je veux déboguer my_functionci-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 invoque cette fonction depuis un shell Unix
  2. Ou en l'appelant directement depuis IPython

Quelle que soit la façon dont je l'invoque, l'interprète s'arrête à la ligne qui dit ipsh(). Une fois que vous avez terminé, vous pouvez le faire Ctrl-Det Python reprendra l'exécution (avec toutes les mises à jour de variables que vous avez effectuées). Notez que, si vous exécutez le code à partir d'un IPython standard le shell IPython (cas 2 ci-dessus), le nouveau shell IPython sera imbriqué dans celui à partir duquel vous l'avez appelé, ce qui est parfaitement bien, mais il est bon d'en être conscient. Quoi qu'il en soit, une fois que l'interpréteur s'arrête à l'emplacement de ipsh, je peux inspecter la valeur de a(quel être 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, il ne vous permet pas d'ajouter ou de supprimer des points d'arrêt une fois que vous avez appelé le script, ce qui est très frustrant. À mon avis, c'est la seule chose qui empêche IPython de devenir un excellent outil de débogage pour Python.

Le mieux que vous puissiez faire pour le moment:

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 (ie a breakpoint). Vous pouvez alors "sauter" entre différents "points d'arrêt" prédéfinis et codés en dur avec Ctrl-D, ce qui quitterait le shell IPython intégré actuel et s'arrêterait à nouveau chaque fois que l'interpréteur frappera le prochain appel à ipsh().

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

Amelio Vazquez-Reina
la source
2
Veuillez le mettre à jour chaque fois que vous trouvez une solution encore meilleure. Mais ça a l'air génial. Je vais l'utiliser.
Phani
1
Où / comment définissez-vous / importez-vous cfg, banner_msg, exit_msg et inspectez-vous?
Gordon Bean
1
Cela semble bien fonctionner pour moi: github.com/kdodia/snippets/blob/master/ipsh.py
karan.dodia
1
J'avais besoin d'ajouter import inspectavant que cela fonctionne. La personnalisation de la ligne de commande semble être interrompue pour moi.
Pascal
7
Pourquoi ne pas simplement utiliser import ipdb; ipdb.set_trace()? Peut-être que quelque chose me manque, mais je ne vois pas l'avantage de votre méthode beaucoup plus compliquée.
luator
19

Vous pouvez démarrer la session IPython à partir de pudb et revenir à la session de débogage comme vous le souhaitez.

BTW, ipdb utilise IPython dans les coulisses et vous pouvez réellement utiliser les fonctionnalités IPython telles que la complétion TAB et les commandes magiques (celle commence par %). Si vous êtes d'accord avec ipdb, vous pouvez le démarrer à partir d'IPython en utilisant des commandes telles que %runet %debug. La session ipdb est en fait meilleure que celle d'IPython ordinaire dans le sens où vous pouvez monter et descendre dans la trace de pile, etc. Que manque-t-il dans ipdb pour "l'inspection d'objets"?

De plus, python.el fourni avec Emacs> = 24.3 a un bon support ipdb.

tkf
la source
1
Merci tkf. Je suis un grand fan de votre package Emacs-Jedi. Quand vous avez dit qu'Emacs 24.3 a un bon support pour ipdb, pourriez-vous élaborer? Je démarre généralement IPython dans un M-x ansi-termtampon séparé , puis j'utilise isend-mode pour lier mes tampons source au tampon IPython afin que je puisse envoyer du code à l'interpréteur IPython avec un raccourci clavier qui envoie automatiquement la %pastemagie au tampon IPython. Cela me permet de tester rapidement des régions dans IPython. J'exécute toujours mes programmes à partir de ce shell IPython avec runet utilise embed()pour arrêter.
Amelio Vazquez-Reina
3
Par exemple, lorsque vous parcourez le code, le code source est ouvert dans l'autre tampon avec une flèche pointant vers le point d'exécution actuel. Vous avez également la commande send-region, comme vous le faites avec isend-mode.
tkf
Merci @tkf Comment puis-je démarrer une ipdbsession de débogage en utilisant python.elet avoir des choses comme send-regionetc. dans le shell correspondant? En général, où puis-je trouver plus d'informations à ce sujet?
Amelio Vazquez-Reina
J'utilise %runou %debug. Je suppose que ça import ipdb; ipdb.set_trace()marche aussi. Je ne pense pas que vous puissiez envoyer plusieurs lignes à ipdb. C'est la limitation d'ipdb. Mais le %pastetruc fonctionne probablement . Vous pouvez envoyer une demande de fonctionnalité à IPython dev.
tkf
13

Il semble que l'approche de la réponse de @ gaborous soit obsolète .

La nouvelle approche semble être:

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

def buggy_method():
    debug()
Elliot Larson
la source
Cela vous permet-il d'utiliser des ipdbinstructions dans le shell ipython, lors du débogage?
alpha_989
6

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 variables, disons que vous avez une variable l, taper "l" dans pdb liste le code, mais! L affiche la valeur de l.

user1953384
la source
2
C'est un méchant pdb gotcha, et il vaut la peine d'être connu.
Wilfred Hughes
4

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 souhaitez définir un point d'arrêt

joyeux
la source
4

Vous pouvez commencer IPython de l'intérieur ipdb !

Induire le ipdbdébogueur 1 :

import idpb; ipdb.set_trace()

Entrez IPython depuis la ipdb>console 2 :

from IPython import embed; embed()

Revenez à la ipdb>console de l'intérieur IPython:

exit

Si vous avez la chance d'utiliser Emacs, les choses peuvent être rendues encore plus pratiques!

Cela nécessite l'utilisation de M-x shell. À l'aide de yasnippetet bm , définissez l'extrait de code suivant. Cela remplacera le texte ipdbdans l'éditeur par la set-traceligne. Après avoir inséré l'extrait de code, la ligne sera mise en surbrillance afin qu'elle soit facilement visible et navigable. Utilisez M-x bm-nextpour naviguer.

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

1 Tout sur une seule ligne pour une suppression facile. Comme importscela n'arrive qu'une seule fois, ce formulaire garantit ipdbqu'il sera importé lorsque vous en aurez besoin sans frais supplémentaires.

2 Vous pouvez vous épargner de la saisie en important IPython dans votre .pdbrcfichier :

try:
    from IPython import embed
except:
    pass

Cela vous permet d'appeler simplement embed()de l'intérieur ipdb(bien sûr, uniquement lorsque IPython est installé).

Lorem Ipsum
la source
3

Une option consiste à utiliser un IDE comme Spyder qui devrait vous permettre d'interagir avec votre code pendant le débogage (en utilisant 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.

Benjamin B.
la source
3

la bonne réponse, facile, cool et exacte à la question est d'utiliser la macro% run avec l'option -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
Ping
la source
Cela devrait être plus haut. Si j'ai besoin d'insérer une ligne dans mon code (dans un module), je dois redémarrer IPython pour recharger le module. Avec cela, je peux juste %save /tmp/foo.py x-yle code que je veux exécuter et déboguer, puis %run -d /tmp/foo.pydéfinir le point d'arrêt où je veux. Très bonne réponse!
Romeo Valentin
2

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

TurinTurambar
la source
2

L' EDI Pyzo a des capacités similaires à celles demandées par l'OP. Vous n'avez pas besoin de démarrer en mode débogage. De la même manière que MATLAB, les commandes sont exécutées dans le shell. Lorsque vous configurez un point d'arrêt dans une ligne de code source, l'EDI arrête l'exécution à cet endroit et vous pouvez également déboguer et émettre des commandes IPython régulières.

Il semble cependant que l'étape ne fonctionne pas (encore?) Bien (c'est-à-dire s'arrêter sur une ligne et ensuite entrer dans une autre fonction) à moins que vous ne définissiez un autre point d'arrêt.

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

user6715080
la source
2

Depuis python 3.2, vous avez la interactcommande, qui vous donne accès à tout l'espace de commande python / ipython.

alpha_989
la source
1

L'exécution depuis l'intérieur du shell IPython d'Emacs et le jeu de points d'arrêt via pdb.set_trace () devraient fonctionner.

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

Andreas Röhler
la source
1

Développer un nouveau code

Débogage dans IPython

  1. Utilisez l'exécution de cellules Jupyter / IPython pour accélérer les itérations d'expérimentation
  2. Utilisez le débogage %% pour parcourir

Exemple de cellule:

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

Débogage du code existant

IPython dans le débogage

  1. Débogage d'un test unitaire interrompu: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. Débogage en dehors du cas de test: breakpoint(), python -m ipdb, etc.
  3. IPython.embed () pour une fonctionnalité IPython complète si nécessaire dans le débogueur

Réflexions sur Python

Je suis d'accord avec l'OP pour dire que beaucoup de choses que MATLAB fait bien Python n'a toujours pas et devrait vraiment le faire puisque à peu près tout dans le langage favorise la vitesse de développement par rapport à la vitesse de production. Peut-être qu'un jour je contribuerai plus que des corrections de bogues triviales à CPython.

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

Voir aussi Est-il possible d'exécuter des commandes dans IPython avec le débogage?

NagerBikeRun
la source