Dieser Artikel ist der Artikel zum 24. Tag von Python Advent Calendar 2016. Hier sind einige Tipps zum Debuggen von Python.
Debuggen Sie, indem Sie den Wert, den Sie überprüfen möchten, mit der Funktion print () in stdout drucken. Wenn dies das Problem löst, ist es besser.
Debuggen Sie FizzBuzz. Angenommen, Sie möchten diesmal FizzBuzz mit einer Zahl von 1 bis 20 ausgeben. Der folgende Code hat einen offensichtlichen Fehler. Verstehst du?
example_fizzbuzz_buggy.py::
for ii in range(1, 21):
if ii % 3 == 0:
print('Fizz')
elif ii % 5 == 0:
print('Buzz')
elif ii % 15 == 0:
print('FizzBuzz')
else:
print(ii)
Wenn Sie dies tun, zeigt example_fizzbuzz.py die folgende Meldung an:
$ python example_fizzbuzz.py
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
Fizz
16
17
Fizz
19
Buzz
Irgendwas stimmt nicht. Es gibt keinen Hinweis auf "FizzBuzz". Für 15 sollten Sie FizzBuzz
sehen. Fügen wir print () hinzu, um das Verhalten bei 15 zu sehen.
for ii in range(1, 21):
print('CURRENT: {}'.format(ii)) #hinzufügen
if ii % 3 == 0:
print('Fizz')
elif ii % 5 == 0:
print('Buzz')
elif ii % 15 == 0:
print('HIT') #hinzufügen
print('FizzBuzz')
else:
print(ii)
Die mit "add" kommentierte Zeile ist der hinzugefügte print (). Ich habe den Code hinzugefügt, um die folgende Debug-Nachricht auszugeben.
HIT
an, wenn die Zahl durch 15 teilbar ist.Machen wir das.
$ python example_fizzbuzz.py
CURRENT: 1
1
CURRENT: 2
2
CURRENT: 3
Fizz
CURRENT: 4
4
CURRENT: 5
Buzz
CURRENT: 6
Fizz
CURRENT: 7
7
CURRENT: 8
8
CURRENT: 9
Fizz
CURRENT: 10
Buzz
CURRENT: 11
11
CURRENT: 12
Fizz
CURRENT: 13
13
CURRENT: 14
14
CURRENT: 15
Fizz
CURRENT: 16
16
CURRENT: 17
17
CURRENT: 18
Fizz
CURRENT: 19
19
CURRENT: 20
Buzz
Wenn "CURRENT: 15" angezeigt wird, wird "Fizz" angezeigt und "HIT" wird nicht angezeigt. Dies bedeutet, dass wir den Block von "if ii% 3 == 0:" und nicht den Block von "elif ii% 15 == 0:" ausführen. Wenn ii 15 ist, ist es natürlich durch 3 teilbar, also "wenn ii% 3 == 0:" wahr ist und das "elif ii% 15 == 0:", nachdem es nicht ausgeführt wurde, weil es "elif" ist. .. Ich fand, dass ii% 15 == 0
vor anderen Bedingungen verzweigen musste, also werde ich den Code korrigieren.
$ python example_fizzbuzz.py
CURRENT: 1
1
CURRENT: 2
2
CURRENT: 3
Fizz
CURRENT: 4
4
CURRENT: 5
Buzz
CURRENT: 6
Fizz
CURRENT: 7
7
CURRENT: 8
8
CURRENT: 9
Fizz
CURRENT: 10
Buzz
CURRENT: 11
11
CURRENT: 12
Fizz
CURRENT: 13
13
CURRENT: 14
14
CURRENT: 15
HIT
FizzBuzz
CURRENT: 16
16
CURRENT: 17
17
CURRENT: 18
Fizz
CURRENT: 19
19
CURRENT: 20
Buzz
Wenn es 15 ist, werden "HIT" und "FizzBuzz" angezeigt. Hört sich gut an. Der Fehler wurde entfernt. Löschen Sie danach das zuvor hinzugefügte print (). Der endgültige Code sieht folgendermaßen aus:
example_fizzbuzz.py::
for ii in range(1, 21):
if ii % 15 == 0:
print('FizzBuzz')
elif ii % 3 == 0:
print('Fizz')
elif ii % 5 == 0:
print('Buzz')
else:
print(ii)
Diese Technik wird üblicherweise in jeder Programmiersprache verwendet. Und es ist leicht zu vergessen, den letzten Druck zu löschen (). Vergiss nicht, es zu löschen.
Beim Debuggen mit print () habe ich die Funktionsweise des Programms überprüft, indem ich eine Debug-Nachricht an stdout gedruckt habe. Ein einfaches Programm kann mit dem Prozess Schritt halten, aber wenn es komplizierter wird, wird es schwierig, die Bewegung allein anhand der Debug-Nachricht zu verstehen. Wenn Sie pdb verwenden, können Sie das Programm ausführen, während Sie es zeilenweise überprüfen, und Sie können den Wert der Variablen zu diesem Zeitpunkt überprüfen, sodass Sie den Wert des Programms genauer überprüfen können. Es wird einfacher sein, den Prozess zu verfolgen.
Führen Sie nun den Buggy "example_fizzbuzz_buggy.py" mit pdb aus. Da pdb eine Standardbibliothek ist, müssen Sie sie nicht mit pip installieren.
$ python -m pdb example_fizzbuzz_buggy.py
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(1)<module>()
-> for ii in range(1, 21):
(Pdb)
Fügen Sie dem Python-Argument -m pdb
hinzu. Das von pdb ausgeführte Skript stoppt bei dem zuerst auszuführenden Code und wartet auf die Eingabe. Führen Sie den Debugger aus, indem Sie hier den Befehl pdb angeben und ENTER eingeben. Details zum Befehl pdb werden in 27.3. Pdb - Python Debugger - Python 3.5.2-Dokumentation erläutert.
Geben Sie n (was als nächstes bedeutet) ein, um Zeile für Zeile auszuführen.
(Pdb) n
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb)
pdb lief eine Zeile und zeigte die nächste auszuführende Zeile an.
Das zu diesem Zeitpunkt angezeigte > /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py (2) <module> ()
hat das folgende Format.
> Dateipfad (Anzahl der ausgeführten Zeilen) Funktionsname ()
Da es sich diesmal nicht um eine Funktion handelt, wird der Funktionsname als "
Wenn nur ENTER (kein Befehl angegeben), wird der vorherige pdb-Befehl ausgeführt. Wenn Sie daher eine andere Zeile verarbeiten möchten, geben Sie ENTER ein.
(Pdb)
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(4)<module>()
-> elif ii % 5 == 0:
(Pdb)
Mit l (Bedeutungsliste) können Sie sehen, wo die Leitung gerade läuft.
(Pdb) l
1 for ii in range(1, 21):
2 if ii % 3 == 0:
3 print('Fizz')
4 -> elif ii % 5 == 0:
5 print('Buzz')
6 elif ii % 15 == 0:
7 print('FizzBuzz')
8 else:
9 print(ii)
[EOF]
(Pdb)
Die nächste mit ->
gekennzeichnete Zeile wird ausgeführt.
Wenn Sie statt Zeile für Zeile fortfahren möchten, geben Sie c an (dh weiter).
(Pdb) c
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
Fizz
16
17
Fizz
19
Buzz
The program finished and will be restarted
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(1)<module>()
-> for ii in range(1, 21):
Ich habe das Programm bis zum Ende ausgeführt. Wenn das Programm bis zum Ende verarbeitet ist, druckt pdb "Das Programm ist beendet und wird neu gestartet" und führt das Programm erneut aus.
Wenn Sie die Verarbeitung durch Angabe der Anzahl der Zeilen beenden möchten, verwenden Sie einen Haltepunkt. Um die Verarbeitung zu überprüfen, wenn ii 15 ist, verwenden Sie den Befehl b (dh break), um den Haltepunkt in der zweiten Zeile festzulegen, die zuerst in der if-Anweisung verzweigt.
(Pdb) b 2
Breakpoint 1 at /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py:2
(Pdb)
Dadurch wird das Programm angehalten, bevor die zweite Zeile verarbeitet wird. Fahren wir mit dem Befehl c fort.
(Pdb) c
1
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb)
Es hat aufgehört. Verwenden Sie den Befehl p (dh Drucken), um den Wert von ii zu ermitteln.
(Pdb) p ii
2
(Pdb)
Es ist 2. Ich möchte die Verarbeitung bei 15 überprüfen, also lassen Sie uns c 13 noch mehrmals ausführen.
(Pdb) c
2
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) c
Fizz
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) c
4
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) c
Buzz
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) c
Fizz
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) c
7
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) c
8
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) c
Fizz
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) c
Buzz
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) c
11
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) c
Fizz
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) c
13
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) c
14
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) p ii
15
(Pdb)
Oben wurde c viele Male eingegeben, aber Sie können auch die Bedingungen zum Stoppen festlegen. Dieses Mal möchte ich, dass Sie aufhören, wenn ii 15 ist. Geben Sie daher die Bedingungen wie folgt an.
(Pdb) b 2, ii == 15
Breakpoint 3 at /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py:2
(Pdb)
Und wenn Sie c ausführen, wird es erst gestoppt, wenn ii 15 ist, sodass Sie c nicht mehrmals eingeben müssen.
(Pdb) c
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb) p ii
15
(Pdb)
Führen Sie danach n Zeile für Zeile aus und überprüfen Sie die Operation.
(Pdb) n
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(3)<module>()
-> print('Fizz')
(Pdb) n
Fizz
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(1)<module>()
-> for ii in range(1, 21):
(Pdb)
Sie können sehen, dass die for-Anweisung in der ersten Zeile nach der Ausführung der dritten Zeile verarbeitet wird (dh, die elif ii% 15 == 0: in der sechsten Zeile wird nicht verarbeitet).
(Pdb) list
1 -> for ii in range(1, 21):
2 B if ii % 3 == 0:
3 print('Fizz')
4 elif ii % 5 == 0:
5 print('Buzz')
6 elif ii % 15 == 0:
7 print('FizzBuzz')
8 else:
9 print(ii)
[EOF]
(Pdb)
Da "ii% 3 == 0" wahr ist, wenn ii 15 ist, habe ich pdb verwendet, um zu bestätigen, dass dieses if-elif-else nicht richtig funktioniert, wenn es 15 ist.
(Pdb) p ii
15
(Pdb) p ii % 3 == 0
True
(Pdb)
Haltepunkte werden mit cl gelöscht (was klar bedeutet).
(Pdb) cl
Clear all breaks? yes
Deleted breakpoint 3 at /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py:2
(Pdb)
Geben Sie s (Bedeutungsschritt) an, um die Verarbeitung der aufgerufenen Funktion zu überprüfen.
example_fizzbuzz_buggy2.py::
def fizzbuzz(num):
if ii % 3 == 0:
print('Fizz')
elif ii % 5 == 0:
print('Buzz')
elif ii % 15 == 0:
print('FizzBuzz')
else:
print(ii)
for ii in range(1, 21):
fizzbuzz(ii)
Versuchen Sie, die Funktion fizzbuzz mit example_fizzbuzz_buggy2.py aufzurufen.
$ python -m pdb example_fizzbuzz_buggy2.py
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy2.py(1)<module>()
-> def fizzbuzz(num):
(Pdb) n
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy2.py(11)<module>()
-> for ii in range(1, 21):
(Pdb) n
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy2.py(12)<module>()
-> fizzbuzz(ii)
(Pdb) p ii
1
Bisher fahren wir mit den zuvor erläuterten Inhalten bis zum Aufruf von fizzbuzz () in der 12. Zeile fort.
Geben Sie als Nächstes s an, um den Prozess in fizzbuzz () zu stoppen.
(Pdb) s
--Call--
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy2.py(1)fizzbuzz()
-> def fizzbuzz(num):
(Pdb) n
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy2.py(2)fizzbuzz()
-> if ii % 3 == 0:
(Pdb)
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy2.py(4)fizzbuzz()
-> elif ii % 5 == 0:
Geben Sie r (bedeutet return) an, um zum Ende der Funktion fizzbuzz () zu gelangen.
(Pdb) r
1
--Return--
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy2.py(9)fizzbuzz()->None
-> print(ii)
(Pdb) n
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy2.py(11)<module>()
-> for ii in range(1, 21):
fizzbuzz () -> None
gibt an, dass fizzbuzz () None zurückgegeben hat.
Ich habe mit python -m pdb
gearbeitet, um pdb zu verwenden, aber jetzt muss ich die Boot-Optionen ändern. Auf diese Weise können Sie pdb nicht verwenden, wenn Sie ein Startskript schreiben oder Python nicht direkt aufrufen.
pdb liefert dafür set_trace (). Wenn set_trace () aufgerufen wird, wechselt es in den pdb-Debug-Modus und wartet auf Befehle.
Setzen Sie set_trace () in example_fizzbuzz_buggy.py.
example_fizzbuzz_buggy.py::
for ii in range(1, 21):
if ii % 3 == 0:
print('Fizz')
elif ii % 5 == 0:
import pdb; pdb.set_trace() #hinzufügen
print('Buzz')
elif ii % 15 == 0:
print('FizzBuzz')
else:
print(ii)
Import pdb; pdb.set_trace ()
in Zeile 5 hinzugefügt.
Führen Sie example_fizzbuzz_buggy.py aus, geben Sie diesmal jedoch nicht -m pdb
an.
$ python example_fizzbuzz_buggy.py
1
2
Fizz
4
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(6)<module>()
-> print('Buzz')
(Pdb)
Es blieb an der 6. Linie stehen.
(Pdb) l
1 for ii in range(1, 21):
2 if ii % 3 == 0:
3 print('Fizz')
4 elif ii % 5 == 0:
5 import pdb; pdb.set_trace()
6 -> print('Buzz')
7 elif ii % 15 == 0:
8 print('FizzBuzz')
9 else:
10 print(ii)
[EOF]
(Pdb)
Die Verarbeitung wird in der Zeile nach set_trace () gestoppt. Mit dieser Methode können Sie pdb starten, ohne die Startoptionen zu ändern.
Dieser Debug-Code muss am Ende des Debugs entfernt werden. Wenn Sie vergessen, es zu löschen, startet pdb und wartet während des Produktionsvorgangs auf Eingaben. Im Gegensatz zum Debuggen mit print () gibt es immer echten Schaden. Wir empfehlen, mit Git-Commit-Hooks und CI-Tests auf PDF-Kontamination zu prüfen.
import pdb; pdb.set_trace ()
hat zwei Anweisungen, eine ist eine import-Anweisung.
Normalerweise ist es vorzuziehen, die Importanweisung am Anfang der Datei zu schreiben.
Dieser Debug-Code muss also am Ende des Debuggens gelöscht werden
Es wird in eine Zeile geschrieben, um die Anzahl der zu löschenden Zeilen zu verringern.
Dies wird auch in der offiziellen Python-Dokumentation vorgestellt.
Es ist üblich, es in einer Zeile zu schreiben.
ipdb ist eine Bibliothek, die die Funktionalität von pdb mithilfe von IPython erweitert.
ipdb wird mit pip installiert.
$ pip install ipdb
Geben Sie zunächst "-m ipdb" als Python-Startoption an oder schreiben Sie "import ipdb; ipdb.set_trace ()" in Ihren Code. Der Code ist hervorgehoben und leicht zu sehen.
Sie können die Attributliste anzeigen, indem Sie auf die Registerkarte klicken.
Davon abgesehen entspricht die Verwendung fast der von pdb.
bpdb ist eine Bibliothek, die die Funktionalität von pdb mithilfe von BPython erweitert. Dies wird als Funktion von BPython selbst bereitgestellt.
bpdb installiert bpython mit pip.
$ pip install bpython
Geben Sie zunächst "-m bpdb" als Python-Startoption an oder schreiben Sie "import bpdb; bpdb.set_trace ()" in Ihren Code. Es verhält sich wie pdb, aber wenn Sie "B" eingeben, wird BPython im aktuellen Stapelrahmen gestartet.
Das Ende von BPython ist "C-d".
PuDB ist ein Hochleistungsdebugger, der auf der Konsole verwendet werden kann.
Es installieren.
pip install pudb
Nach der Installation können Sie den Befehl pudb3 (für Python3) verwenden. Geben Sie zunächst den Dateipfad an, den Sie in pudb3 ausführen möchten.
$ pudb3 example_fizzbuzz_buggy.py
Der Debugger-Bereich entspricht der Arbeit mit pdb. Sie können "C-x" verwenden, um zwischen dem Debugger-Bereich und dem interaktiven Shell-Bereich zu wechseln.
Sie können das Thema usw. festlegen und die Einstellungswerte werden in ~ / .config / pudb / pudb.cfg gespeichert.
~/.config/pudb/pudb.cfg::
[pudb]
breakpoints_weight = 1
current_stack_frame = top
custom_stringifier =
custom_theme =
display = auto
line_numbers = True
prompt_on_quit = True
seen_welcome = e027
shell = classic
sidebar_width = 0.5
stack_weight = 1
stringifier = type
theme = dark vim
variables_weight = 1
wrap_variables = True
PyCharm ist eine von JetBrains entwickelte Python-IDE. Sie können den Debugger mit GUI-Operationen betreiben. Es ist sehr intuitiv und wunderbar. Sie können das Installationsprogramm von https://www.jetbrains.com/pycharm/download/ herunterladen.
Öffnen Sie die Datei und versuchen Sie, einen Haltepunkt festzulegen.
Wenn Sie auf die rechte Seite der Zeilennummernanzeige klicken, wird ein roter Kreis angezeigt. Dies ist der Debugger. Es ist das gleiche wie VS oder Eclipse.
Führen Sie über das Menü [Ausführen]> [Debuggen](oder Fehlermarkierung) aus )Klicken.
Fehlerzeichen
Wenn es ausgeführt wird, stoppt es am Haltepunkt und die Stapelrahmen und Variablen werden im Bereich angezeigt.
Da das Symbol für detaillierte Vorgänge aktiviert ist, können Sie es durch Klicken bedienen. Wenn Sie mit der Maus darüber fahren, wird das zu erledigende Symbol angezeigt.
Sie können die Remote-Debug-Funktion auch in der Professional Edition verwenden.
Wenn Sie in unittest einen Fehler erhalten, aber die Ursache nicht kennen, sollten Sie mit Testcode debuggen. In diesem Fall können Sie "pdb.set_trace ()" in den Testcode einfügen und pdb starten.
Der folgende Testcode startet beispielsweise pdb, wenn der Test ausgeführt wird.
test_main.py::
from unittest import TestCase
def create_message(count):
return 'Fish: {}'.format(count)
class SimpleTest(TestCase):
def test_it(self):
import pdb; pdb.set_trace() #Debug-Code
msg = create_message(1)
self.assertEqual(msg, 'Fish: 1')
Wenn Sie unittest ausführen, wird pdb gestartet.
$ python -m unittest
> /working/advent-calendar-2016-python/test_main.py(11)test_it()
-> msg = create_message(1)
(Pdb) list
6
7
8 class SimpleTest(TestCase):
9 def test_it(self):
10 import pdb; pdb.set_trace() #Debug-Code
11 -> msg = create_message(1)
12 self.assertEqual(msg, 'Fish: 1')
[EOF]
(Pdb)
Nase ist ein beliebtes Testframework. Geben Sie "--nocapture" an, wenn der Test wie zuvor mit pdb vorzeitig gestoppt werden soll.
$ nosetests --nocapture
> /working/advent-calendar-2016-python/test_main.py(11)test_it()
-> msg = create_message(1)
(Pdb)
Es bietet auch "--pdb", "--pdb-Fehler" und "--pdb-Fehler", um pdb im Falle eines Fehlers oder Fehlers zu starten.
$ nosetests --pdb
> /Users/sximada/ng2/var/lib/miniconda3/envs/py3.5.2/lib/python3.5/unittest/case.py(665)fail()
-> raise self.failureException(msg)
(Pdb)
In der Nasen-Dokumentation heißt es: "Warnung Nase selbst unterstützt Python 3, viele Plugins von Drittanbietern jedoch nicht!" Es scheint, dass die Python3-Unterstützung des Nasen-Plugins nicht erweitert ist, nicht die Nase selbst.
pytest ist auch ein beliebtes Testframework wie die Nase. Wenn Sie pytest so ausführen, wie es ist, wird pdb gestartet, wenn set_trace () ausgeführt wird.
$ pytest
================================================================================ test session starts ================================================================================
platform darwin -- Python 3.5.2, pytest-3.0.5, py-1.4.32, pluggy-0.4.0
rootdir: /working/advent-calendar-2016-python, inifile:
plugins: celery-4.0.0
collected 1 items
test_main.py
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PDB set_trace (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /working/advent-calendar-2016-python/test_main.py(11)test_it()
-> msg = create_message(1)
(Pdb)
Sie können den Debugger bei einem Fehler starten, indem Sie "--pdb" angeben.
$ pytest --pdb
================================================================================ test session starts ================================================================================
platform darwin -- Python 3.5.2, pytest-3.0.5, py-1.4.32, pluggy-0.4.0
rootdir: /working/advent-calendar-2016-python, inifile:
plugins: celery-4.0.0
collected 2 items
test_main.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
self = <test_main.SimpleTest testMethod=test_error>
def test_error(self):
msg = create_message(1)
> self.assertEqual(msg, 'ERROR')
E AssertionError: 'Fish: 1' != 'ERROR'
E - Fish: 1
E + ERROR
test_main.py:15: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /Users/sximada/ng2/var/lib/miniconda3/envs/py3.5.2/lib/python3.5/unittest/case.py(665)fail()
-> raise self.failureException(msg)
(Pdb)
Django ist ein beliebtes Webframework.
Verwenden von pdb mit Django
Wenn es sich um einen Entwicklungsserver handelt (manage.py runserver
), ist nichts schwierig.
Erstellen Sie ein Projekt und starten Sie pdb.
Die Struktur des Projekts ist wie folgt.
$ tree proj
proj
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
Das proj / urls.py ist wie folgt geschrieben.
from django.conf.urls import url
from django.http import HttpResponse
def top_view(request):
import pdb; pdb.set_trace()
return HttpResponse('OK')
urlpatterns = [
url(r'^$', top_view),
]
Wir haben eine Ansicht definiert, die beim Zugriff auf "/" eine Anfrage mit "OK" zurückgibt. (Da es schwierig war, die Datei zu teilen, wird die Ansicht auch in urls.py beschrieben.) Pdb.set_trac () wird in der View-Funktion beschrieben.
Starten Sie den Entwicklungsserver mit runserver.
$ python manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
December 24, 2016 - 13:45:26
Django version 1.11.dev20161224024349, using settings 'proj.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Senden Sie eine Anfrage an die URL.
$ curl http://127.0.0.1:8000/
Der Entwicklungsserver startet pdb mit pdb.set_trace () und wartet auf Eingabe.
> /working/advent-calendar-2016-python/proj/urls.py(7)top_view()
-> return HttpResponse('OK')
(Pdb)
Alles was Sie tun müssen, ist zu debuggen. Sie können auch den Inhalt des Anforderungsobjekts überprüfen. Andere WAFs können häufig auch mit pdb debuggt werden.
gunicorn ist ein beliebter WSGI-HTTP-Server. Führen Sie das Django-Projekt früher unter gunicorn aus.
$ gunicorn proj.wsgi:application
[2016-12-24 22:53:59 +0900] [8915] [INFO] Starting gunicorn 19.6.0
[2016-12-24 22:53:59 +0900] [8915] [INFO] Listening at: http://127.0.0.1:8000 (8915)
[2016-12-24 22:53:59 +0900] [8915] [INFO] Using worker: sync
[2016-12-24 22:53:59 +0900] [8918] [INFO] Booting worker with pid: 8918
Senden Sie eine Anfrage an die URL.
$ curl http://127.0.0.1:8000/
pdb.set_trace () startet pdb.
> /working/advent-calendar-2016-python/proj/urls.py(7)top_view()
-> return HttpResponse('OK')
(Pdb)
Es ist dasselbe wie der Django-Entwicklungsserver.
Gunicorn hat eine Einstellung zum Zeitlimit für die Anforderung, die standardmäßig 30 Sekunden beträgt. Zeigen Sie (Pdb) [2016-12-24 23:09:37 +0900] [9102] [CRITICAL] WORKER TIMEOUT (pid: 9115)
an, während Sie pdb starten und debuggen
Wenn pdb beendet wird, wird die Anforderung als Zeitüberschreitung behandelt. Legen Sie daher mit "--timeout" einen großen Zeitüberschreitungswert fest und führen Sie ihn aus.
$ gunicorn proj.wsgi:application --timeout 9999999
[2016-12-24 23:13:11 +0900] [9126] [INFO] Starting gunicorn 19.6.0
[2016-12-24 23:13:11 +0900] [9126] [INFO] Listening at: http://127.0.0.1:8000 (9126)
[2016-12-24 23:13:11 +0900] [9126] [INFO] Using worker: sync
[2016-12-24 23:13:11 +0900] [9130] [INFO] Booting worker with pid: 9130
> /working/advent-calendar-2016-python/proj/urls.py(7)top_view()
-> return HttpResponse('OK')
(Pdb)
Sellerie ist eine häufig verwendete Aufgabenwarteschlange. Ich werde Redis dieses Mal als Broker verwenden, also starte es.
$ nohup redis-server 2>&1 > /dev/null &
[1] 9384
Erstellen Sie die Datei task.py und definieren Sie die Aufgabe add (). Dieses Mal möchten wir diese add () - Task debuggen.
tasks.py::
from celery import Celery
app = Celery('tasks', broker='pyamqp://guest@localhost//')
@app.task
def add(x, y):
import pdb; pdb.set_trace()
return x + y
Starten Sie den Arbeiter.
$ celery -A tasks.app worker
-------------- [email protected] v4.0.0 (latentcall)
---- **** -----
--- * *** * -- Darwin-16.1.0-x86_64-i386-64bit 2016-12-24 23:23:27
-- * - **** ---
- ** ---------- [config]
- ** ---------- .> app: tasks:0x1042b4940
- ** ---------- .> transport: redis://127.0.0.1:6379//
- ** ---------- .> results: disabled://
- *** --- * --- .> concurrency: 4 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
Feuern Sie die Aufgabe in einem anderen Terminal ab.
>>> import tasks
>>> tasks.add.delay(1, 2)
<AsyncResult: a07399f4-e28a-4471-b57d-30ce1cb3abf4>
>>>
Ich erhalte eine bdb.BdbQuit-Ausnahme in einem Terminal, in dem ein Worker ausgeführt wird.
[2016-12-24 23:24:50,006: WARNING/PoolWorker-4] > /working/advent-calendar-2016-python/tasks.py(9)add()
-> return x + y
[2016-12-24 23:24:50,007: WARNING/PoolWorker-4](Pdb)
[2016-12-24 23:24:50,061: ERROR/PoolWorker-4] Task tasks.add[a07399f4-e28a-4471-b57d-30ce1cb3abf4] raised unexpected: BdbQuit()
Traceback (most recent call last):
File "/Users/sximada/ng2/var/lib/miniconda3/envs/py3.5.2/lib/python3.5/site-packages/celery/app/trace.py", line 368, in trace_task
R = retval = fun(*args, **kwargs)
File "/Users/sximada/ng2/var/lib/miniconda3/envs/py3.5.2/lib/python3.5/site-packages/celery/app/trace.py", line 623, in __protected_call__
return self.run(*args, **kwargs)
File "/working/advent-calendar-2016-python/tasks.py", line 9, in add
return x + y
File "/working/advent-calendar-2016-python/tasks.py", line 9, in add
return x + y
File "/Users/sximada/ng2/var/lib/miniconda3/envs/py3.5.2/lib/python3.5/bdb.py", line 48, in trace_dispatch
return self.dispatch_line(frame)
File "/Users/sximada/ng2/var/lib/miniconda3/envs/py3.5.2/lib/python3.5/bdb.py", line 67, in dispatch_line
if self.quitting: raise BdbQuit
bdb.BdbQuit
Das Debuggen von Sellerie finden Sie unter http://docs.celeryproject.org/en/latest/userguide/debugging.html. Sie können pdb nicht unverändert verwenden. Sie müssen celery.contrib.rdb verwenden und den Debugger über Telnet verwenden. Ändern Sie die Datei task.py.
tasks.py::
from celery import Celery
from celery.contrib import rdb
app = Celery('tasks', broker='redis://127.0.0.1/')
@app.task
def add(x, y):
rdb.set_trace()
return x + y
Die Aufgabe neu entzünden.
>>> import tasks
>>> tasks.add.delay(1,2)
<AsyncResult: 42b88871-a679-492f-bfc9-1f86043dab33>
>>>
Der Worker zeigt dann die Meldung "Remote Debugger: 6900: Warten auf Client ..." an. Greifen Sie mit Telnet auf diese Portnummer zu.
$ telnet 127.0.0.1 6900
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
> /working/advent-calendar-2016-python/tasks.py(10)add()
-> return x + y
(Pdb) p x
1
(Pdb) p y
2
(Pdb)
pdb wartet auf Eingabe. Der Rest ist der gleiche wie der Betrieb von pdb. Der Worker schließt den Port, wenn die Aufgabe abgeschlossen ist. Wenn Sie es mehrmals ausführen, müssen Sie jedes Mal eine neue Verbindung mit Telnet herstellen.
Im Fall von Sellerie ist add (1, 2) eine normale Funktionsausführung, daher ist es auch eine gute Idee, damit zu debuggen. Der Code von Celery wird jedoch weiterhin ausgeführt. Wenn Sie ihn also für die Ausführung als Funktion vorbereiten, die keine Aufgabe ist, Es ist einfacher zu isolieren, ob es sich um ein Sellerieproblem handelt.
Das Jupyter-Notizbuch bietet einen magischen Befehl "% debug", mit dem Sie pdb starten können. Das Folgende ist das Debuggen von request.get ('http://example.com') mit pdb.
Circle CI ist ein beliebter CI-Dienst. Manchmal schlägt ein Test auf Circle CI fehl und die Ursache kann nicht ermittelt werden. In diesem Fall können Sie, wenn Sie zum Erstellen "Mit SSH neu erstellen" auswählen, SSH in den Build-Container einfügen und das Verhalten überprüfen.
Ich klone das Repository in mein Home-Verzeichnis, also füge ich dort "pdb.set_trace ()" in den Code ein Durch manuelles Ausführen des Tests können Sie die Fehlerursache leichter ermitteln.
Dies ist nicht auf Python beschränkt. Wenn Sie jedoch eine Staging-Umgebung haben, in der Sie sich mit ssh anmelden können, Sie möchten pdb in einer Staging-Umgebung ausführen, oder?
Zum Beispiel für eine Django-Anwendung Schreiben Sie pdb.set_trace () beim Staging und starten Sie den Entwicklungsserver manuell mit einer entsprechenden Portnummer. Es ist schnell und einfach, eine Verbindung zum Entwicklungsserver mit SSH-Port vorwärts herzustellen.
[staging]$ python manage.py runserver 4649
Verbinden Sie localhost: 8000 mit Staging: 4649 mit ssh-Port vorwärts.
$ ssh -L 8000:localhost:4649 staging
Senden Sie dann die Anfrage, als würden Sie auf localhost testen.
Lass uns absolut aufhören. Ich habe einen Unfall (Erklärung). Wenn Sie eine RC-Umgebung haben, können Sie dies tun, aber Wenn Sie jedoch nicht genau aufpassen, können Sie es nicht sehen.
Recommended Posts