Cet article est l'article du 24ème jour du Calendrier de l'Avent Python 2016. Voici quelques conseils pour déboguer Python.
Déboguez en imprimant la valeur que vous souhaitez vérifier sur stdout à l'aide de la fonction print (). Si cela résout le problème, c'est mieux que ça.
Déboguer FizzBuzz. Cette fois, je veux sortir FizzBuzz en utilisant un nombre de 1 à 20. Le code ci-dessous a un bug évident. Comprenez vous?
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)
Lorsque vous faites cela, example_fizzbuzz.py affichera le message suivant:
$ python example_fizzbuzz.py
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
Fizz
16
17
Fizz
19
Buzz
Quelque chose ne va pas. Il n'y a aucune indication de «FizzBuzz». Pour 15, vous devriez voir FizzBuzz
. Ajoutons print () pour voir le comportement à 15.
for ii in range(1, 21):
print('CURRENT: {}'.format(ii)) #ajouter à
if ii % 3 == 0:
print('Fizz')
elif ii % 5 == 0:
print('Buzz')
elif ii % 15 == 0:
print('HIT') #ajouter à
print('FizzBuzz')
else:
print(ii)
La ligne commentée ʻadd` est l'ajout de print (). J'ai ajouté le code pour afficher le message de débogage suivant.
--Affichez le numéro actuel comme CUURENT: 1
--Affiche HIT
lorsque le nombre est divisible par 15.
Faisons le.
$ 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
Lorsque «CURRENT: 15» est affiché, «Fizz» s'affiche et «HIT» n'est pas affiché. Cela signifie que nous exécutons le bloc de ʻif ii% 3 == 0: et non le bloc de ʻelif ii% 15 == 0:
. Si ii vaut 15, il est naturellement divisible par 3, donc ʻif ii% 3 == 0: est vrai, et ʻelif ii% 15 == 0: ʻaprès il n'est pas exécuté car il est ʻelif
. .. J'ai trouvé que ʻii% 15 == 0` devait brancher avant d'autres conditions, donc je vais corriger le code.
$ 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
S'il est 15, il affiche «HIT» et «FizzBuzz». Ça m'a l'air bien. Le bogue a été supprimé. Après cela, supprimez le print () ajouté précédemment. Le code final ressemble à ceci:
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)
Cette technique est couramment utilisée dans tout langage de programmation. Et il est facile d'oublier de supprimer la dernière impression (). N'oubliez pas de le supprimer.
Lors du débogage à l'aide de print (), j'ai vérifié le fonctionnement du programme en imprimant un message de débogage sur stdout. Un programme simple peut suivre le processus, mais à mesure qu'il se complique, il devient difficile de comprendre le mouvement à partir du seul message de débogage. Si vous utilisez pdb, vous pouvez exécuter le programme en le vérifiant ligne par ligne, et vous pouvez vérifier la valeur de la variable à ce moment-là, donc c'est plus programmatique. Il sera plus facile de suivre le processus.
Maintenant, lancez ʻexample_fizzbuzz_buggy.py` avec le bogue mentionné précédemment en utilisant pdb. Puisque pdb est une bibliothèque standard, vous n'avez pas besoin de l'installer avec pip.
$ 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)
Ajoutez -m pdb
à l'argument Python. Les scripts exécutés sur pdb s'arrêtent au premier code qu'ils exécutent et attendent l'entrée. Utilisez le débogueur en spécifiant ici la commande pdb et en entrant ENTRÉE. Les détails de la commande pdb sont expliqués dans 27.3. Pdb - Python Debugger - Python 3.5.2 Documentation.
Entrez n (signifiant suivant) pour exécuter ligne par ligne.
(Pdb) n
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb)
pdb a exécuté une ligne et a affiché la ligne suivante à exécuter.
Le > /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py (2) <module> ()
affiché à ce moment a le format suivant.
> Chemin du fichier (nombre de lignes en cours d'exécution) Nom de la fonction ()
Puisqu'il ne s'agit pas d'une fonction cette fois, le nom de la fonction s'affiche sous la forme «
Si seulement ENTER (aucune commande n'est spécifiée), la commande pdb précédente sera exécutée. Par conséquent, si vous souhaitez traiter une autre ligne, entrez ENTRÉE.
(Pdb)
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(4)<module>()
-> elif ii % 5 == 0:
(Pdb)
Vous pouvez utiliser l (liste de significations) pour voir où la ligne est actuellement exécutée.
(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)
La ligne suivante marquée par «->» est exécutée. Si vous voulez continuer au lieu de ligne par ligne, spécifiez c (ce qui signifie continuer).
(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):
J'ai couru le programme jusqu'à la fin. Lorsque le programme est traité jusqu'à la fin, pdb imprime "Le programme est terminé et sera redémarré" et réexécute le programme.
Si vous souhaitez arrêter le traitement en spécifiant le nombre de lignes, utilisez un point d'arrêt. Pour vérifier le traitement lorsque ii est égal à 15, utilisez la commande b (signifiant break) pour définir le point d'arrêt sur la deuxième ligne qui se branche en premier dans l'instruction if.
(Pdb) b 2
Breakpoint 1 at /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py:2
(Pdb)
Cela mettra le programme en pause avant que la deuxième ligne ne soit traitée. Continuons le processus en utilisant la commande c.
(Pdb) c
1
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(2)<module>()
-> if ii % 3 == 0:
(Pdb)
Cela s'est arrêté. Utilisez la commande p (qui signifie imprimer) pour voir quelle est la valeur de ii.
(Pdb) p ii
2
(Pdb)
C'est 2. Je veux vérifier le traitement à 15, alors exécutons c 13 fois de plus.
(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)
Dans ce qui précède, c a été entré plusieurs fois, mais vous pouvez également définir les conditions pour arrêter. Cette fois, je veux que vous vous arrêtiez lorsque ii a 15 ans, alors spécifiez les conditions comme suit.
(Pdb) b 2, ii == 15
Breakpoint 3 at /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py:2
(Pdb)
Et lorsque vous exécutez c, il ne s'arrête que lorsque ii est égal à 15, vous n'avez donc pas à taper c plusieurs fois.
(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)
Après cela, exécutez n ligne par ligne et vérifiez l'opération.
(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)
Vous pouvez voir que l'instruction for sur la première ligne est traitée après l'exécution de la troisième ligne (c'est-à-dire que ʻelif ii% 15 == 0: ʻa la sixième ligne n'est pas traité).
(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)
Puisque ʻii% 3 == 0` est vrai quand ii vaut 15, j'ai confirmé en utilisant pdb que ce if-elif-else ne fonctionne pas correctement quand il est 15.
(Pdb) p ii
15
(Pdb) p ii % 3 == 0
True
(Pdb)
Les points d'arrêt sont supprimés en utilisant cl (ce qui signifie clear).
(Pdb) cl
Clear all breaks? yes
Deleted breakpoint 3 at /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py:2
(Pdb)
Pour vérifier le traitement de la fonction appelée, spécifiez s (étape de signification).
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)
Essayez d'entrer la fonction fizzbuzz avec example_fizzbuzz_buggy2.py.
$ 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
Jusqu'à présent, en utilisant le contenu expliqué précédemment, nous procédons au processus jusqu'à l'appel fizzbuzz () sur la 12ème ligne.
Ensuite, spécifiez s pour arrêter le processus dans fizzbuzz ().
(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:
Pour passer à la fin de la fonction fizzbuzz (), spécifiez r (signifiant retour).
(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
indique que fizzbuzz () a renvoyé None.
J'utilisais python -m pdb
pour utiliser pdb, mais maintenant je dois changer les options de démarrage. Cela ne vous permet pas d'utiliser pdb si vous écrivez un script de démarrage ou n'appelez pas directement Python.
pdb fournit set_trace () pour cela. Lorsque set_trace () est appelé, il passe en mode de débogage pdb et attend les commandes.
Mettez set_trace () dans 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() #ajouter à
print('Buzz')
elif ii % 15 == 0:
print('FizzBuzz')
else:
print(ii)
Ajout de ʻimport pdb; pdb.set_trace () à la ligne 5. Exécutez example_fizzbuzz_buggy.py, mais cette fois ne spécifiez pas
-m pdb`.
$ python example_fizzbuzz_buggy.py
1
2
Fizz
4
> /working/advent-calendar-2016-python/example_fizzbuzz_buggy.py(6)<module>()
-> print('Buzz')
(Pdb)
Il s'est arrêté à la 6ème ligne.
(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)
Le traitement est arrêté à la ligne suivant set_trace (). Vous pouvez utiliser cette méthode pour démarrer pdb sans modifier les options de démarrage.
Ce code de débogage doit être supprimé à la fin du débogage. Si vous oubliez de le supprimer, pdb démarrera et attendra l'entrée pendant l'opération de production. Contrairement au débogage avec print (), il y a toujours un vrai mal. Nous vous recommandons de vérifier la contamination de pdb avec les hooks de validation et les tests CI de Git.
ʻImport pdb; pdb.set_trace () `a deux instructions, l'une est une instruction d'importation. Normalement, il est préférable d'écrire l'instruction d'importation au début du fichier, Ce code de débogage doit être effacé à la fin du débogage, donc Il est écrit sur une seule ligne pour réduire le nombre de lignes à effacer. Ceci est également introduit dans la documentation officielle de Python, Il est d'usage de l'écrire sur une seule ligne.
ipdb est une bibliothèque qui étend les fonctionnalités de pdb à l'aide d'IPython.
ipdb est installé avec pip.
$ pip install ipdb
Commencez par spécifier -m ipdb
comme option de démarrage Python ou en écrivant ʻimport ipdb; ipdb.set_trace ()` dans votre code.
Le code est mis en évidence et facile à voir.
Vous pouvez afficher la liste des attributs en appuyant sur l'onglet.
À part cela, l'utilisation est presque la même que pdb.
bpdb est une bibliothèque qui étend les fonctionnalités de pdb à l'aide de BPython. Ceci est fourni en tant que fonctionnalité de BPython lui-même.
bpdb installe bpython avec pip.
$ pip install bpython
Commencez par spécifier -m bpdb
comme option de démarrage Python ou en écrivant ʻimport bpdb; bpdb.set_trace ()` dans votre code.
Il se comporte comme pdb, mais taper «B» lance BPython au frame de pile actuel.
La fin de BPython est C-d
.
PuDB est un débogueur hautes performances qui peut être utilisé sur la console.
Installez-le.
pip install pudb
Une fois installé, vous pourrez utiliser la commande pudb3 (pour Python3). Commencez par spécifier le chemin du fichier que vous souhaitez exécuter dans pudb3.
$ pudb3 example_fizzbuzz_buggy.py
Le volet du débogueur est identique à l'utilisation de pdb. Vous pouvez utiliser C-x
pour basculer entre le volet du débogueur et le volet du shell interactif.
Vous pouvez définir le thème, etc., et les valeurs des paramètres sont enregistrées dans ~ / .config / pudb / pudb.cfg.
~/.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 est un IDE Python développé par JetBrains. Vous pouvez faire fonctionner le débogueur avec des opérations GUI. C'est très intuitif et merveilleux. Vous pouvez télécharger le programme d'installation à partir de https://www.jetbrains.com/pycharm/download/.
Ouvrez le fichier et essayez de définir un point d'arrêt.
Si vous cliquez sur le côté droit de l'affichage du numéro de ligne, un cercle rouge apparaît. C'est le débogueur. C'est la même chose que VS ou Eclipse.
Exécutez à partir du menu [Exécuter]> [Déboguer], (ou marque de bogue )Cliquez sur.
Marque de bogue
Une fois exécuté, il s'arrêtera au point d'arrêt et les cadres de pile et les variables seront affichés dans le volet.
Puisque l'icône est activée pour des opérations détaillées, vous pouvez la faire fonctionner en cliquant dessus, et si vous passez la souris dessus, l'icône à faire s'affiche.
Vous pouvez également utiliser la fonction de débogage à distance dans Professional Edition.
Si vous obtenez une erreur dans unittest mais que vous ne connaissez pas la cause, vous voudrez déboguer avec le code de test.
Dans ce cas, vous pouvez mettre pdb.set_trace ()
dans le code de test et démarrer pdb.
Par exemple, le code de test suivant démarre pdb lorsque le test est exécuté.
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() #Code de débogage
msg = create_message(1)
self.assertEqual(msg, 'Fish: 1')
Lancer unittest lancera pdb.
$ 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() #Code de débogage
11 -> msg = create_message(1)
12 self.assertEqual(msg, 'Fish: 1')
[EOF]
(Pdb)
nose est un framework de test populaire.
Si vous voulez que le test soit arrêté prématurément en utilisant pdb comme auparavant, spécifiez --nocapture
.
$ nosetests --nocapture
> /working/advent-calendar-2016-python/test_main.py(11)test_it()
-> msg = create_message(1)
(Pdb)
Il fournit également --pdb
, --pdb-failures
et --pdb-errors
pour démarrer pdb en cas d'erreur ou d'échec.
$ 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)
Comme le dit la documentation du nez, "Warning nose lui-même supporte python 3, mais de nombreux plugins tiers ne le font pas! Il semble que le support Python3 pour le plugin nose ne soit pas avancé, pas pour le nose lui-même.
pytest est également un framework de test populaire comme nose. Si vous exécutez pytest tel quel, pdb démarrera lorsque set_trace () sera exécuté.
$ 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)
Vous pouvez démarrer le débogueur en cas d'erreur en spécifiant --pdb
.
$ 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 est un framework Web populaire.
Utilisation de pdb avec Django
S'il s'agit d'un serveur de développement (manage.py runserver
), rien n'est difficile.
Créez un projet et démarrez pdb.
La structure du projet est la suivante.
$ tree proj
proj
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
Le proj / urls.py est écrit comme suit.
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),
]
Nous avons défini une vue qui renvoie une requête avec ʻOK lors de l'accès
/ `.
(Comme il était difficile de diviser le fichier, la vue est également décrite dans urls.py)
Pdb.set_trac () est décrit dans la fonction View.
Démarrez le serveur de développement avec 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.
Envoyez une demande à l'URL.
$ curl http://127.0.0.1:8000/
Le serveur de développement démarre pdb avec pdb.set_trace () et attend l'entrée.
> /working/advent-calendar-2016-python/proj/urls.py(7)top_view()
-> return HttpResponse('OK')
(Pdb)
Tout ce que vous avez à faire est de déboguer. Vous pouvez également vérifier le contenu de l'objet de requête. D'autres WAF peuvent également être débogués avec pdb.
gunicorn est un serveur HTTP WSGI populaire. Exécutez le projet Django plus tôt sur gunicorn.
$ 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
Envoyez une demande à l'URL.
$ curl http://127.0.0.1:8000/
pdb.set_trace () lance pdb.
> /working/advent-calendar-2016-python/proj/urls.py(7)top_view()
-> return HttpResponse('OK')
(Pdb)
C'est le même que le serveur de développement Django.
Gunicorn a un paramètre pour expirer la demande, qui est de 30 secondes par défaut. Afficher (Pdb) [2016-12-24 23:09:37 +0900] [9102] [CRITICAL] WORKER TIMEOUT (pid: 9115)
lors du démarrage de pdb et du débogage
Si pdb se termine, la demande est traitée comme un timeout, alors définissez une valeur de timeout importante avec --timeout
et exécutez.
$ 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)
Celery est une file d'attente de tâches couramment utilisée. J'utiliserai Redis en tant que courtier cette fois, alors démarrez-le.
$ nohup redis-server 2>&1 > /dev/null &
[1] 9384
Créez tasks.py et définissez la tâche add (). Cette fois, nous voulons déboguer cette tâche add ().
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
Démarrez le travailleur.
$ 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
Lancez la tâche dans un autre terminal.
>>> import tasks
>>> tasks.add.delay(1, 2)
<AsyncResult: a07399f4-e28a-4471-b57d-30ce1cb3abf4>
>>>
J'obtiens une exception bdb.BdbQuit dans un terminal exécutant un worker.
[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
En fait, comment déboguer Celery est décrit à http://docs.celleryproject.org/en/latest/userguide/debugging.html. Vous ne pouvez pas utiliser pdb tel quel, vous devez utiliser celery.contrib.rdb et utiliser le débogueur via telnet. Modifiez tasks.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
Relancez la tâche.
>>> import tasks
>>> tasks.add.delay(1,2)
<AsyncResult: 42b88871-a679-492f-bfc9-1f86043dab33>
>>>
Le worker affichera alors le message "Débogueur distant: 6900: En attente du client ...". Accédez à ce numéro de port avec telnet.
$ 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 attend une entrée. Le reste est le même que le fonctionnement de pdb. Le worker ferme le port lorsque la tâche est terminée. Si vous l'exécutez plusieurs fois, vous devrez vous reconnecter à telnet à chaque fois.
Dans le cas de Celery, add (1, 2) est une exécution de fonction normale, c'est donc aussi une bonne idée de déboguer avec cela. Cependant, le code de Celery est toujours exécuté, donc si vous le rendez exécutable en tant que fonction qui n'est pas une tâche, Il sera plus facile d'isoler s'il s'agit d'un problème de céleri.
Le notebook jupyter fournit une commande magique % debug
qui vous permet de démarrer pdb.
Ce qui suit est le débogage de requests.get ('http://example.com') avec pdb.
Circle CI est un service CI populaire.
Parfois, un test exécuté sur Circle CI échoue et la cause ne peut être déterminée.
Dans ce cas, si vous sélectionnez Reconstruire avec SSH
pour construire, vous pouvez SSH dans le conteneur de construction et vérifier le comportement.
J'ai cloné le dépôt dans mon répertoire personnel, donc j'ai mis pdb.set_trace ()
dans le code là
L'exécution manuelle du test permet de déterminer plus facilement la cause de l'erreur.
Cela ne se limite pas à Python, mais si vous avez un environnement de test où vous pouvez vous connecter avec ssh, Vous voulez exécuter pdb sur un environnement de préparation, non?
Par exemple, pour une application Django Écrivez pdb.set_trace () lors du transfert et démarrez manuellement le serveur de développement avec un numéro de port approprié. Il est rapide et facile de se connecter au serveur de développement avec le transfert de port ssh.
[staging]$ python manage.py runserver 4649
Connectez localhost: 8000 à staging: 4649 avec le transfert de port ssh.
$ ssh -L 8000:localhost:4649 staging
Envoyez ensuite la demande comme si vous testiez sur localhost.
Arrêtons absolument. J'ai un accident (déclaration). Si vous avez un environnement RC, vous pouvez le faire, mais Pourtant, si vous ne faites pas très attention, vous ne pourrez pas le voir.
Recommended Posts