Je débogue un script python, et je veux regarder une variable pour un changement (un peu comme vous pouvez regarder une adresse mémoire dans gdb). Existe-t-il un moyen de le faire?
Pour regarder une variable lorsque vous atteignez un point d'arrêt , vous pouvez utiliser la commande commands
. Par exemple. impression some_variable
lorsque vous atteignez le point d'arrêt # 1 ( exemple canonique de pdb
doc ).
(Pdb) commands 1
(com) print some_variable
(com) end
(Pdb)
Mise à jour pour Python 3
(Pdb) commands 1
(com) print(some_variable)
(com) end
(Pdb)
De plus, vous pouvez utiliser la commande condition
pour vous assurer que le point d'arrêt n'est atteint que lorsque la variable prend une certaine valeur.
par exemple:
(Pdb) condition 1 some_variable==some_value
Voici une façon vraiment hacky de le faire avec pdb
. Ces commandes peuvent être placées dans votre ~/.pdbrc
pour un chargement automatique à chaque fois que vous utilisez pdb
.
!global __currentframe, __stack; from inspect import currentframe as __currentframe, stack as __stack
!global __copy; from copy import copy as __copy
!global __Pdb; from pdb import Pdb as __Pdb
!global __pdb; __pdb = [__framerec[0].f_locals.get("pdb") or __framerec[0].f_locals.get("self") for __framerec in __stack() if (__framerec[0].f_locals.get("pdb") or __framerec[0].f_locals.get("self")).__class__ == __Pdb][-1]
alias _setup_watchpoint !global __key, __dict, __val; __key = '%1'; __dict = __currentframe().f_locals if __currentframe().f_locals.has_key(__key) else __currentframe().f_globals; __val = __copy(%1)
alias _nextwatch_internal next;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_nextwatch_internal %1")
alias _stepwatch_internal step;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_stepwatch_internal %1")
alias nextwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_nextwatch_internal"])
alias stepwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_stepwatch_internal"])
Cela ajoute deux commandes, nextwatch
et stepwatch
qui prennent chacune un nom de variable varname comme argument. Ils feront une copie superficielle de la variable locale du cadre actuel pour varname si possible, et continueront d'exécuter next
ou step
respectivement jusqu'à ce que ce nom pointe vers des changements.
Cela fonctionne dans CPython 2.7.2 mais repose sur quelques pdb
internes donc il se cassera probablement ailleurs.
Pour Python 3 :
vous pouvez utiliser display la fonctionnalité de pdb
Une fois que vous avez atteint le point d'arrêt, tapez simplement
ipdb> affichage expression
exemple:
ipdb> display instance
display instance: <AppUser: dmitry4>
ipdb> display instance.id
display instance.id: 9
ipdb> display instance.university
display instance.university: <University: @domain.com>
ipdb> display
Currently displaying:
instance.university: <University: @domain.com>
instance.id: 9
instance: <AppUser: dmitry4>
ipdb>
comme vous pouvez le voir, chaque fois que vous tapez display, il imprime toutes vos montres (expressions). Vous pouvez utiliser la fonction intégrée undisplay
pour supprimer certaines montres.
Vous pouvez également utiliser l'expression pp pour joliment imprimer l'expression (très utile)
Une solution possible consiste à utiliser pdb ++ :
pip install pdbpp
Ensuite, "marquez" l'objet que vous souhaitez regarder avec le décorateur @pdb.break_on_setattr
:
from pdb import break_on_setattr
@break_on_setattr('bar')
class Foo(object):
pass
f = Foo()
f.bar = 42 # the program breaks here
Ici, pdb
se cassera lors de tout changement de l'attribut bar
sur n'importe quel objet Foo.
Mises en garde
Seules les invocations de la méthode __setattr__
Sous-jacente déclencheront le point d'arrêt. Cela signifie que f.bar = 'XYZ'
Et setattr(f, 'XYZ')
fonctionneront, mais la manipulation de l'objet bar
- ne déclenchera pas le point d'arrêt:
f.bar = []
f.bar.append(7) # will NOT trigger breakpoint
f.bar = 2
f.bar += 5 # will trigger breakpoint
Remarque: @break_on_setattr
Ne fait pas partie du module pdb
standard. pdb
est remplacé/patché par le singe par le package pdbpp
.
Vous pouvez également envelopper un objet existant (via sa classe) après pdb.set_trace()
:
(Pdb++) import pdb
(Pdb++) pdb.break_on_setattr('tree_id')(self.__class__)
(Pdb++) continue