Conseils de débogage Python

Conseils de débogage Python

Cet article est l'article du 24ème jour du Calendrier de l'Avent Python 2016. Voici quelques conseils pour déboguer Python.

Déboguer avec print ()

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.

Vérifiez le comportement du programme ligne par ligne à l'aide de pdb

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 «». (Affiche en fait la valeur de \ _ \ _ nom \ _ \ _)

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.

Écrivez ʻimport pdb; pdb.set_trace () ʻ dans votre code

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.

Déboguer avec ipdb

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.

スクリーンショット 2016-12-24 20.52.10.png

Vous pouvez afficher la liste des attributs en appuyant sur l'onglet.

スクリーンショット 2016-12-24 20.52.34.png

À part cela, l'utilisation est presque la même que pdb.

Déboguer avec bpdb

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.

スクリーンショット 2016-12-24 21.16.30.png

La fin de BPython est C-d.

Déboguer avec pudb

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.

スクリーンショット 2016-12-25 0.15.41.png

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

Déboguer avec PyCharm

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.

スクリーンショット 2016-12-25 0.37.53.png

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.

スクリーンショット 2016-12-25 0.41.05.png

Marque de bogue

スクリーンショット 2016-12-25 0.41.26.png

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.

スクリーンショット 2016-12-25 0.38.40.png

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.

スクリーンショット 2016-12-25 0.42.37.png

Vous pouvez également utiliser la fonction de débogage à distance dans Professional Edition.

Déboguer dans diverses scènes

Si vous souhaitez utiliser le débogueur avec unittest

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)

Si vous souhaitez utiliser le débogueur avec nez

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.

Si vous souhaitez utiliser le débogueur avec pytest

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)

Si vous souhaitez utiliser un débogueur avec Django

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.

Si vous souhaitez utiliser le débogueur avec gunicorn

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.

Attention aux timeouts

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)

Si vous souhaitez utiliser un débogueur avec Celery

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.

Si vous souhaitez utiliser le débogueur avec le notebook jupyter

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.

スクリーンショット 2016-12-25 0.05.23.png

Si vous souhaitez utiliser le débogueur avec CircleCI

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.

スクリーンショット 2016-12-24 22.20.56.png

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.

Si vous souhaitez utiliser le débogueur dans l'environnement intermédiaire

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.

Si vous souhaitez utiliser un débogueur dans un environnement de production

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

Conseils de débogage Python
astuces python
Astuces Python
Astuces Python
Conseils Python Conda
débogage python DS
Astuces de clic Python
De manière inattendue (?) Connaissance du bean Python
Astuces Python et Numpy
[Python] Débogage super utile
Astuces Python (mon mémo)
Conseils d'installation de Python PyTorch
[Python] Débogage plus efficace!
Conseils pour réfléchir à np.newaxis en Python / Numpy
Python
Débogage avec pdb en Python
Recevoir des conseils d'entrée standard @ python
[Astuces] Gérez Athena avec Python
[Python + Selenium] Conseils pour le grattage
Python | Jupyter> Débogage post-mortem | pdb.post_mortem ()
Conseils relatifs aux API Google Drive (Python)
~ Conseils pour les débutants de Python présentés avec amour par Pythonista ③ ~
Compréhension complète du débogage Python
Conseils sur l'entrée / la sortie de fichier Python
[TouchDesigner] Conseils pour la déclaration par python
Conseils pour appeler Python à partir de C
Écrire une liste de python astuces vim rapides
Conseils pour remplacer et déboguer les fonctions
python kafka
Les bases de Python ⑤
Résumé Python
Python intégré
Notation d'inclusion Python
Technique Python
Compte à rebours Python 2.7
Mémorandum Python
Python FlowFishMaster
Service Python
Conseils pour faciliter la lecture des documents d'audition Python
fonction python ①
Les bases de Python
Mémo Python
ufo-> python (3)
Notation d'inclusion Python
Le tour de Jupyter 4
Installer python
Python Singleton
Les bases de Python ④
Mémorandum Python 2
mémo python
Python Jinja2
Incrément Python
atCoder 173 Python
[Python] fonction
Installation de Python
astuces numpy