Schrittweises Debuggen mit IPython

170

Nach dem, was ich gelesen habe, gibt es zwei Möglichkeiten, Code in Python zu debuggen:

  • Mit einem traditionellen Debugger wie pdboder ipdb. Dies unterstützt Befehle wie cfor continue, nfor step-over, sfor step-intousw.), Sie haben jedoch keinen direkten Zugriff auf eine IPython-Shell, die für die Objektprüfung äußerst nützlich sein kann.

  • Verwenden von IPython durch Einbetten einer IPython-Shell in Ihren Code. Sie können dies tun from ipython import embedund dann embed()in Ihrem Code verwenden. Wenn Ihr Programm / Skript eine embed()Anweisung trifft , werden Sie in eine IPython-Shell verschoben. Dies ermöglicht die vollständige Überprüfung von Objekten und das Testen von Python-Code unter Verwendung aller IPython-Extras. Bei Verwendung können embed()Sie den Code jedoch nicht mehr Schritt für Schritt mit praktischen Tastaturkürzeln durchlaufen.

Gibt es eine Möglichkeit, das Beste aus beiden Welten zu kombinieren? Dh

  1. Die Lage sein, Schritt- für -Schritt durch den Code mit praktischen PDB / IPDB Tastenkombinationen.
  2. Haben Sie bei einem solchen Schritt (z. B. bei einer bestimmten Anweisung) Zugriff auf eine vollwertige IPython-Shell .

IPython-Debugging wie in MATLAB:

Ein Beispiel für diese Art des "erweiterten Debuggens" finden Sie in MATLAB, wo der Benutzer immer vollen Zugriff auf die MATLAB-Engine / -Shell hat und dennoch Schritt für Schritt durch ihren Code gehen, bedingte Haltepunkte definieren usw. Von Was ich mit anderen Benutzern besprochen habe, ist die Debugging-Funktion, die die Leute am meisten vermissen, wenn sie von MATLAB zu IPython wechseln.

IPython-Debugging in Emacs und anderen Editoren:

Ich möchte die Frage nicht zu spezifisch machen, aber ich arbeite hauptsächlich in Emacs, daher frage ich mich, ob es eine Möglichkeit gibt, diese Funktionalität in sie zu integrieren. Im Idealfall ermöglicht Emacs (oder der Editor) dem Programmierer, Haltepunkte an einer beliebigen Stelle im Code festzulegen und mit dem Interpreter oder Debugger zu kommunizieren, damit dieser an dem Ort Ihrer Wahl stoppt und zu einem vollständigen IPython-Interpreter an diesem Ort gebracht wird.

Amelio Vazquez-Reina
quelle
pdb hat einen !Befehl, der jeden Python-Befehl am Haltepunkt ausführt
Dmitry Galchinsky
5
Ich suche auch einen Python-Debugger ähnlich wie Matlab! Zum Beispiel mache ich viel Prototyping in der Python-Shell. Alle Variablen werden mit der Shell gespeichert. Jetzt stoße ich auf ein Problem. Ich hoffe, ein kleines Stück Code mit diesen berechneten Variablen mit der Shell zu debuggen. Ein neuer Debugger kann jedoch nicht auf alte Variablen zugreifen. Es ist nicht praktisch für das Prototyping.
user1914692
1
Für Emacs-Benutzer bietet RealGUD eine unglaublich gute Benutzeroberfläche.
Clément
1
Thanks @ Clément Ich habe das Repo im letzten Monat verfolgt und bin sehr gespannt auf das Projekt :) Ich habe es noch nicht ausprobiert, aber wenn ich es einmal tue (oder wenn Sie es tun), können Sie hier vielleicht eine Antwort schreiben zeigt, wie man das erreicht, was angefordert wird. Für andere als Referenz ist die URL github.com/rocky/emacs-dbgr
Amelio Vazquez-Reina
@ Clément Auch wenn Sie Erfahrung mit RealGUD & ipdb haben, habe ich versucht, es wie hier beschrieben zu verwenden, github.com/rocky/emacs-dbgr/issues/96 ohne Glück.
Amelio Vazquez-Reina

Antworten:

60

Sie können die %pdbMagie von IPython verwenden . Rufen Sie einfach %pdbIPython auf und wenn ein Fehler auftritt, werden Sie automatisch zu weitergeleitet ipdb. Während Sie nicht sofort den Schritt haben, sind Sie ipdbdanach drin.

Dies erleichtert das Debuggen einzelner Funktionen, da Sie einfach eine Datei mit laden %loadund dann eine Funktion ausführen können. Sie könnten einen Fehler mit einem assertan der richtigen Position erzwingen .

%pdbist eine Linienmagie. Nennen Sie es , wie %pdb on, %pdb 1, %pdb offoder %pdb 0. Wenn es ohne Argument aufgerufen wird, funktioniert es als Umschalter.

Sebastian
quelle
6
^ Dies ist die wahre Antwort
Cesar
Gibt es etwas Ähnliches, bei dem pdb IPython-ähnliche Funktionen hat, ohne anfangs in IPython zu sein?
Lucas
4
Sie können das Programm auch mit starten ipython --pdb file.py -- argsund werden in einer Ausnahme auf ipdb abgelegt. Könnte es wert sein, die Antwort zu ergänzen.
Sebastian
110

Was ist mit ipdb.set_trace ()? In Ihrem Code:

import ipdb; ipdb.set_trace()

Update : Jetzt können wir in Python 3.7 schreiben breakpoint(). Es funktioniert genauso, aber es gehorcht auch der PYTHONBREAKPOINTUmgebungsvariablen. Diese Funktion stammt aus diesem PEP .

Dies ermöglicht eine vollständige Überprüfung Ihres Codes und Sie haben Zugriff auf Befehle wie c(Weiter), n(Nächste Zeile ausführen), s(Schritt in die Methode an Punkt) und so weiter.

Siehe das ipdb-Repo und eine Liste der Befehle . IPython heißt jetzt Jupyter (bearbeiten: Teil von) .


ps: Beachten Sie, dass ein ipdb-Befehl Vorrang vor Python-Code hat. Um zu schreiben list(foo), brauchst du also print list(foo).

Wenn Ihnen die ipython-Eingabeaufforderung gefällt (Emacs und Vim-Modi, Verlauf, Vervollständigungen usw.), ist es auch einfach, dasselbe für Ihr Projekt zu erhalten, da es auf dem Python-Eingabeaufforderungs-Toolkit basiert .

Ehvince
quelle
10
Dies ist der Weg, um es zu tun.
j08lue
Meine Antwort enthält nun , wie die Verwendung ipdbin Emacs RealGUDund isend-modegenau das zu tun , was der OP fragt.
Amelio Vazquez-Reina
1
Jupyter ist kein Ersatz für IPython, sondern für IPython Notebook. Jupyter-Notebooks verwenden im Hintergrund den Kernel. Bei Python-Notebooks ist der Kernel normalerweise ein IPython-Kernel. Das IPython-Projekt wird fortgesetzt.
FA
1
breakpoint()ist wunderbar. In PyCharm werden Sie sogar in den PyCharm-Debugger versetzt. Es ist auch eine schnelle Möglichkeit, den PyCharm-Debugger über Funktionen aufzurufen, die in die Konsole eingefügt wurden.
Richard Möhn
40

(Update am 28. Mai 2016) Verwenden von RealGUD in Emacs

Für jeden in Emacs zeigt dieser Thread , wie man alles erreicht, was im OP (und mehr) beschrieben ist

  1. Ein neuer wichtiger Debugger in Emacs namens RealGUD, der mit jedem Debugger (einschließlich ipdb) betrieben werden kann.
  2. Das Emacs-Paket isend-mode.

Die Kombination dieser beiden Pakete ist äußerst leistungsfähig und ermöglicht es, genau das im OP beschriebene Verhalten wiederherzustellen und noch mehr zu tun.

Weitere Infos zum Wiki-Artikel von RealGUD für ipdb.


Ursprüngliche Antwort:

Nachdem ich viele verschiedene Methoden zum Debuggen von Python ausprobiert habe, einschließlich aller in diesem Thread erwähnten Methoden, ist eine meiner bevorzugten Methoden zum Debuggen von Python mit IPython eingebettete Shells.

Definieren einer benutzerdefinierten eingebetteten IPython-Shell:

Fügen Sie Ihrem Skript Folgendes hinzu PYTHONPATH, damit die Methode ipsh()verfügbar wird.

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)

Wenn ich dann etwas in meinem Code debuggen möchte, platziere ich es ipsh()genau an der Stelle, an der ich eine Objektinspektion usw. durchführen muss. Angenommen, ich möchte my_functionunten debuggen

Es benutzen:

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

und dann rufe ich auf my_function(2)eine der folgenden Arten auf:

  1. Entweder durch Ausführen eines Python-Programms, das diese Funktion von einer Unix-Shell aus aufruft
  2. Oder indem Sie es direkt von IPython aus aufrufen

Unabhängig davon, wie ich es aufrufe, bleibt der Dolmetscher an der Zeile stehen, in der steht ipsh(). Sobald Sie fertig sind, können Sie dies tun Ctrl-Dund Python setzt die Ausführung fort (mit allen von Ihnen vorgenommenen Variablenaktualisierungen). Beachten Sie, dass, wenn Sie den Code von einem regulären IPython aus ausführen, der IPython-Shell (Fall 2 oben), die neue IPython-Shell in derjenige verschachtelt ist, von der aus Sie sie aufgerufen haben. Dies ist vollkommen in Ordnung, aber es ist gut, sich dessen bewusst zu sein. Sobald der Interpreter an der Stelle von anhält ipsh, kann ich den Wert von a(welche sein 2) überprüfen , sehen, welche Funktionen und Objekte definiert sind usw.

Das Problem:

Die obige Lösung kann verwendet werden, um Python an einer beliebigen Stelle in Ihrem Code anzuhalten und Sie dann in einen vollwertigen IPython-Interpreter zu verschieben. Leider können Sie nach dem Aufrufen des Skripts keine Haltepunkte hinzufügen oder entfernen, was sehr frustrierend ist. Meiner Meinung nach ist dies das einzige, was IPython daran hindert, ein großartiges Debugging-Tool für Python zu werden.

Das Beste, was Sie jetzt tun können:

Eine Problemumgehung besteht darin, ipsh()a priori an den verschiedenen Stellen zu platzieren, an denen der Python-Interpreter eine IPython-Shell starten soll (z. B. a breakpoint). Sie können dann zwischen verschiedenen vordefinierten, fest codierten "Haltepunkten" mit "springen" Ctrl-D, wodurch die aktuell eingebettete IPython-Shell verlassen und erneut gestoppt wird, wenn der Interpreter den nächsten Aufruf von anruft ipsh().

Wenn Sie diesen Weg gehen, können Sie den "Debugging-Modus" beenden und alle nachfolgenden Haltepunkte ipshell.dummy_mode = Trueignorieren, ipshellindem Python alle nachfolgenden Instanziierungen des oben erstellten Objekts ignoriert .

Amelio Vazquez-Reina
quelle
2
Bitte aktualisieren Sie dies, wenn Sie eine noch bessere Lösung finden. Aber das sieht gut aus. Ich werde es benutzen.
Phani
1
Wo / wie definieren / importieren Sie cfg, banner_msg, exit_msg und inspizieren?
Gordon Bean
1
Dies scheint gut für mich zu funktionieren: github.com/kdodia/snippets/blob/master/ipsh.py
karan.dodia
1
Ich musste hinzufügen, import inspectbevor es funktionierte. Die Anpassung der Befehlszeile scheint für mich jedoch fehlerhaft zu sein.
Pascal
7
Warum nicht einfach benutzen import ipdb; ipdb.set_trace()? Vielleicht vermisse ich etwas, aber ich sehe nicht den Vorteil Ihrer viel komplizierteren Methode.
Luator
19

Sie können die IPython-Sitzung von pudb aus starten und zur Debugging-Sitzung zurückkehren, wie Sie möchten .

Übrigens verwendet ipdb IPython hinter den Kulissen und Sie können tatsächlich IPython-Funktionen wie TAB-Vervollständigung und magische Befehle verwenden (der beginnt mit %). Wenn Sie mit ipdb einverstanden sind, können Sie es von IPython aus mit Befehlen wie %runund starten %debug. Die IPdB-Sitzung ist tatsächlich besser als die einfache IPython-Sitzung in dem Sinne, dass Sie im Stack-Trace usw. auf und ab gehen können. Was fehlt in der IPdb für die "Objektinspektion"?

Außerdem bietet python.el, das mit Emacs> = 24.3 gebündelt ist, eine gute IPdb-Unterstützung.

tkf
quelle
1
Danke tkf. Ich bin ein großer Fan Ihres Emacs-Jedi-Pakets. Wenn Sie sagten, dass Emacs 24.3 eine gute Unterstützung für ipdb hat, würde es Ihnen etwas ausmachen, dies näher zu erläutern? Normalerweise starte ich IPython in einem separaten M-x ansi-termPuffer und binde dann den Isend-Modus , um meine Quellpuffer an den IPython-Puffer zu binden, damit ich Code mit einer Tastenkombination an den IPython-Interpreter senden kann, die die %pasteMagie automatisch an den IPython-Puffer sendet . Dadurch kann ich Regionen in IPython schnell testen. Ich führe meine Programme immer über diese IPython-Shell aus runund verwende sie embed()zum Stoppen.
Amelio Vazquez-Reina
3
Wenn Sie beispielsweise den Code durchlaufen, wird der Quellcode im anderen Puffer geöffnet, wobei der Pfeil auf den aktuellen Ausführungspunkt zeigt. Sie haben auch den Befehl send-region, wie Sie es im isend-Modus tun.
tkf
Danke @tkf Wie kann ich eine ipdbDebugging-Sitzung mit der entsprechenden Shell starten python.elund Dinge wie send-regionetc. haben ? Wo finde ich im Allgemeinen weitere Informationen dazu?
Amelio Vazquez-Reina
Ich benutze %runoder %debug. Ich denke import ipdb; ipdb.set_trace()funktioniert auch. Ich glaube nicht, dass Sie mehrere Zeilen an ipdb senden können. Das ist die Einschränkung von ipdb. Aber wahrscheinlich %pastefunktioniert Trick. Möglicherweise möchten Sie eine Funktionsanforderung an IPython dev senden.
tkf
13

Der Ansatz in der Antwort von @ gaborous scheint veraltet zu sein .

Der neue Ansatz scheint zu sein:

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

def buggy_method():
    debug()
Elliot Larson
quelle
ipdbKönnen Sie auf diese Weise beim Debuggen Anweisungen in der ipython-Shell verwenden?
Alpha_989
6

Ein "!" Das Symbol für Befehle, die Sie in pdb eingeben, scheint den gleichen Effekt zu haben wie etwas in einer IPython-Shell. Dies funktioniert für den Zugriff auf die Hilfe für eine bestimmte Funktion oder sogar für Variablennamen. Vielleicht hilft Ihnen das bis zu einem gewissen Grad. Beispielsweise,

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

Mit! Help (numpy.transpose) erhalten Sie jedoch die erwartete Hilfeseite zu numpy.transpose. Angenommen, Sie haben eine Variable l. Wenn Sie in pdb "l" eingeben, wird der Code aufgelistet, aber! L gibt den Wert von l aus.

user1953384
quelle
2
Dies ist eine böse pdb Gotcha und es lohnt sich, darüber Bescheid zu wissen.
Wilfred Hughes
4

Hast du diesen Tipp ausprobiert ?

Oder noch besser, verwenden Sie ipython und rufen Sie an:

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

dann kannst du einfach benutzen

debug_here()

wann immer Sie einen Haltepunkt setzen möchten

gaborous
quelle
4

Sie können IPython von innen beginnen ipdb !

Induzieren Sie den ipdbDebugger 1 :

import idpb; ipdb.set_trace()

Geben Sie IPython von innen in die ipdb>Konsole 2 ein :

from IPython import embed; embed()

ipdb>Kehren Sie von innen zur Konsole zurück IPython:

exit

Wenn Sie das Glück haben, Emacs zu verwenden, können Sie die Dinge noch bequemer machen!

Dies erfordert die Verwendung M-x shell. Definieren Sie mit yasnippetund bm das folgende Snippet. Dadurch wird der Text ipdbim Editor durch die set-traceZeile ersetzt. Nach dem Einfügen des Snippets wird die Linie hervorgehoben, damit sie leicht erkennbar und navigierbar ist. Verwenden Sie M-x bm-nextzu navigieren.

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

1 Alles in einer Zeile zum einfachen Löschen. Da importsdieses Formular nur einmal vorkommt, wird sichergestellt ipdb, dass es bei Bedarf ohne zusätzlichen Aufwand importiert wird.

2 Sie können sich das Tippen ersparen, indem Sie IPython in Ihre .pdbrcDatei importieren :

try:
    from IPython import embed
except:
    pass

Auf diese Weise können Sie einfach embed()von innen anrufen ipdb(natürlich nur, wenn IPython installiert ist).

Lorem Ipsum
quelle
3

Eine Möglichkeit besteht darin, eine IDE wie Spyder zu verwenden, mit der Sie beim Debuggen mit Ihrem Code interagieren können (tatsächlich über eine IPython-Konsole). Tatsächlich ist Spyder sehr MATLAB-artig, was vermutlich beabsichtigt war. Dazu gehören Variableninspektoren, Variablenbearbeitung, integrierter Zugriff auf Dokumentation usw.

Benjamin B.
quelle
3

Die richtige, einfache, coole und genaue Antwort auf die Frage ist die Verwendung von% run macro mit dem Flag -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
Klingeln
quelle
Dies sollte höher sein. Wenn ich eine Zeile in meinen Code einfügen muss (innerhalb eines Moduls), muss ich IPython neu starten, um das Modul neu zu laden. Damit kann ich nur %save /tmp/foo.py x-yden Code, den ich ausführen und debuggen möchte, und dann %run -d /tmp/foo.pyden Haltepunkt setzen, wo immer ich möchte. Gute Antwort!
Romeo Valentin
2

Wenn Sie exit () in die embedded () -Konsole eingeben, fahren Sie mit dem Code fort und fahren Sie mit der nächsten embedded () -Zeile fort.

TurinTurambar
quelle
2

Die Pyzo IDE verfügt über ähnliche Funktionen wie das OP. Sie müssen nicht im Debug-Modus starten. Ähnlich wie bei MATLAB werden die Befehle in der Shell ausgeführt. Wenn Sie einen Haltepunkt in einer Quellcodezeile einrichten, stoppt die IDE die Ausführung dort und Sie können auch reguläre IPython-Befehle debuggen und ausgeben.

Es scheint jedoch, dass das Einsteigen (noch?) Nicht gut funktioniert (dh in einer Zeile anhalten und dann in eine andere Funktion eintreten), es sei denn, Sie richten einen anderen Haltepunkt ein.

Aus MATLAB kommend, scheint dies die beste Lösung zu sein, die ich gefunden habe.

user6715080
quelle
2

Ab Python 3.2 haben Sie den interactBefehl, mit dem Sie auf den gesamten Python / Ipython-Befehlsbereich zugreifen können.

alpha_989
quelle
1

Das Ausführen von Emacs 'IPython-Shell und dem über pdb.set_trace () festgelegten Haltepunkt sollte funktionieren.

Überprüft mit python-mode.el, Mx ipython RET usw.

Andreas Röhler
quelle
1

Neuen Code entwickeln

Debuggen in IPython

  1. Verwenden Sie die Ausführung von Jupyter / IPython-Zellen, um die Iterationen von Experimenten zu beschleunigen
  2. Verwenden Sie %% debug für den Schritt durch

Zellbeispiel:

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

Vorhandenen Code debuggen

IPython beim Debuggen

  1. Debuggen eines defekten Unit-Tests: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. Debuggen von außerhalb des Testfalls: breakpoint(), python -m ipdbusw.
  3. IPython.embed () für die volle IPython-Funktionalität, wo dies im Debugger erforderlich ist

Gedanken zu Python

Ich stimme dem OP zu, dass viele Dinge, die MATLAB gut macht, Python immer noch nicht hat und wirklich sollte, da fast alles in der Sprache die Entwicklungsgeschwindigkeit gegenüber der Produktionsgeschwindigkeit bevorzugt. Vielleicht werde ich eines Tages mehr als nur triviale Fehlerbehebungen zu CPython beitragen.

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

Siehe auch Ist es möglich, Befehle in IPython mit Debugging auszuführen?

SwimBikeRun
quelle