Que fait ce code ? # Example 1 def main(): for i in range(1, 4): i /= 4 if i == 4: print('Salut PyConFR 2017') if __name__ == '__main__': main() 4 / 107
Que fait ce code ? # Example 1 def main(): for i in range(1, 4): i /= 4 if i == 4: print('Salut PyConFR 2017') if __name__ == '__main__': main() $ python example1.py $ 5 / 107
La sacro-sainte fonction print() # Example 2 def main(): for i in range(1, 4): i /= 4 print(i) if i == 4: print('Salut PyConFR 2017') $ python example2.py 0 0 0 $ 8 / 107
Utilisons logging # Example 3 import logging def main(): logging.basicConfig(level=logging.INFO) logging.info('start program') logging.debug("before loop") for i in range(1, 4): i /= 4 logging.debug(i) logging.debug("after loop") if i == 4: print("Hello PyConFR 2017") logging.debug("Hello PyConFR 2017") logging.info('end program') if __name__ == '__main__': main() INFO:root:start program INFO:root:end program 11 / 107
Utilisons logging INFO:root:start program DEBUG:root:before loop DEBUG:root:0 DEBUG:root:0 DEBUG:root:0 DEBUG:root:after loop INFO:root:end program Obligé de modifier le niveau de log pour voir ce qu'il se passe. 14 / 107
PDB Python Debugger est l'outil natif pour aider les développeurs à trouver et corriger les bugs de leur logiciel fétiche (ou celui que l'on récupère d'un ancien collègue ou prestataire...) 18 / 107
Comment faire import pdb; pdb.set_trace() # Example 5 import pdb def main(): for i in range(1, 4): i /= 4 if i == 4: print("Hello PyConFR 2017") if __name__ == '__main__': pdb.set_trace() main() 21 / 107
Comment faire import pdb; pdb.set_trace() # Example 5 import pdb def main(): for i in range(1, 4): i /= 4 if i == 4: print("Hello PyConFR 2017") if __name__ == '__main__': pdb.set_trace() main() $ python example5.py > example5.py(7)main() -> for i in range(1, 4): 22 / 107
Prenons un exemple lisant un .csv import pendulum import csv class Event: def __init__(self, name, date): self.name = name self.date = pendulum.parse(date) def get_events(stream): for row in csv.reader(stream): yield Event(*row) def main(): with open('events.csv') as stream: for event in get_events(stream): print(event.name, event.date) if __name__ == '__main__': main() 27 / 107
Prenons un exemple lisant un .csv import pendulum import csv class Event: def __init__(self, name, date): self.name = name self.date = pendulum.parse(date) def get_events(stream): for row in csv.reader(stream): yield Event(*row) def main(): with open('events.csv') as stream: for event in get_events(stream): print(event.name, event.date) if __name__ == '__main__': main() PyCon France,2017-09-21 PyCon Canada,2017-11-18 PyCon US,2017-05-17 Python FOSDEM,2018-02-03 Python TOTO,x 28 / 107
Prenons un exemple lisant un .csv $ python events.py Non d'un petit Python, CRASH avec deux exceptions :/ PyCon France 2017-09-21T00:00:00+00:00 PyCon Canada 2017-11-18T00:00:00+00:00 PyCon US 2017-05-17T00:00:00+00:00 Python FOSDEM 2018-02-03T00:00:00+00:00 File "/home/stephane/.virtualenvs/flask/lib/python3.6/site-packages/dateutil/par raise ValueError("Unknown string format") ValueError: Unknown string format During handling of the above exception, another exception occurred: File "/home/stephane/.virtualenvs/flask/lib/python3.6/site-packages/pendulum/par raise ParserError('Invalid date string: {}'.format(text)) pendulum.parsing.exceptions.ParserError: Invalid date string: x 29 / 107
On saute dans le code -> def main(): (Pdb) ll 15 -> def main(): 16 with open('events.csv') as stream: 17 for event in get_events(stream): 18 print(event.name, event.date) 19 print(Event.id) (Pdb) step > events.py(16)main() -> with open('events.csv') as stream: (Pdb) ll 15 def main(): 16 -> with open('events.csv') as stream: 17 for event in get_events(stream): 18 print(event.name, event.date) 19 print(Event.id) 34 / 107
On saute dans le code (Pdb) next > events.py(17)main() -> for event in get_events(stream): (Pdb) ll 15 def main(): 16 with open('events.csv') as stream: 17 -> for event in get_events(stream): 18 print(event.name, event.date) 19 print(Event.id) (Pdb) step --Call-- > events.py(11)get_events() -> def get_events(stream): (Pdb) ll 11 -> def get_events(stream): 12 for row in csv.reader(stream): 13 yield Event(*row) (Pdb) next > events.py(12)get_events() -> for row in csv.reader(stream): (Pdb) ll 11 def get_events(stream): 12 -> for row in csv.reader(stream): 13 yield Event(*row) (Pdb) 35 / 107
Breakpoints Désactiver un breakpoint (Pdb) disable 1 Disabled breakpoint 1 at events.py:9 (Pdb) break Num Type Disp Enb Where 1 breakpoint keep no at events.py:9 42 / 107
Breakpoints Activer un breakpoint (Pdb) enable 1 Enabled breakpoint 1 at events.py:9 (Pdb) break Num Type Disp Enb Where 1 breakpoint keep yes at events.py:9 44 / 107
Nouvelles commandes avec alias alias d pp dir(%1) (Pdb) b 9 Breakpoint 1 at events.py:9 (Pdb) c > events.py(9)__init__() -> self.date = pendulum.parse(date) 49 / 107
Nouvelles commandes avec alias alias d pp dir(%1) (Pdb) b 9 Breakpoint 1 at events.py:9 (Pdb) c > events.py(9)__init__() -> self.date = pendulum.parse(date) (Pdb) d self.__dict__ ['__class__', '__contains__', '__delattr__', ... ] 50 / 107
Nouvelles commandes avec alias alias printdict for key, value in %1.items(): print(f"{key}:{value}") (Pdb) printdict self.__dict__ id:1 name:PyCon France date:2017-09-21T00:00:00+00:00 54 / 107
Breakpoint et une action Possibilité de définir une action lors d'un breakpoint commands [bpnumber] p self.__dict__ printdict self.__dict__ end 57 / 107
Examiner Commande Description p(rint) expr Affiche la valeur de l'expression pp expr Pretty-print de la valeur de l'expression l(ist) Affiche une partie du code ll Affiche toute le code de la fonction a(rgs) Affiche la liste des arguments de la fonction whatis arg Affiche le type de l'argument where Mais ou se trouve l'exception source Affiche la source d'une fonction 61 / 107
Naviguer Commande Description n(ext) Exécute l'instruction s(tep) Exécute et rentre dans la fonction/méthode r(eturn) Continue l'exécution jusqu'au return c(ontinue) Continue l'exécution jusqu'à un breakpoint u(p) Remonte d'un niveau dans la stack trace d(own) Descend d'un niveau dans la stack trace j(ump) lineno Saute a la ligne 62 / 107
Breakpoints Commande Description b(reak) lineno Place un breakpoint à lineno b(reak) lineno, cond Place un breakpoint à lineo si cond est True b(reak) file:lineno Place un breakpoint dans le fichier file à la ligne lineno b(reak) func Place un breakpoint à la première ligne de la fonction func tbreak lineno Place un breakpoint et l'enleve dès que l'on y est 64 / 107
Breakpoints Commande Description clear bpnumber Supprime un breakpoint disable bpnumber Désactive un breakpoint enable bpnumber Active un breakpoint ignore bpnumber count Ignore le breakpoint X fois condition bpnumber condition Permet de modifer le breakpoint 65 / 107
Celles que vous ne connaissez pas ;-) Commande Description !expr Execute une expression dans l'interpreteur Python alias Créer une nouvelle commande PDB unalias Supprime un alias commands [bpnumber] Assigne des commandes à un breakpoint restart or run Redémarre le script 66 / 107
Fichier de con guration alias d pp dir(%1) alias ps d self.__dict__ alias loc !list(locals().keys()) alias printdict for key, value in %1.items(): print(f"{key}:{value}") Voir $HOME/.pdbrc ou ./.pdbrc 67 / 107
pdb ou ipdb avec du dev Web Par exemple, Django django-pdb avec --ipdb ou --pdb ou simplement python -m pdb ./manage.py test python -m ipdb ./manage.py test python -m pudb ./manage.py test 77 / 107
GNU Debugger Également nommé gdb, GNU Debugger, est le débuggeur standard du projet GNU, portable et fonctionne pour plusieurs langages, comme le C, le C++ et aussi le Python ;-) 78 / 107
GNU Debugger Avoir les symboles de debug de Python $ dnf install python3-debug ou $ ./configure --prefix=$PWD-build --with-pydebug $ make $ make install 81 / 107
Que faire avec gdb ? Commande Description py-bt Montre la stack trace Python py-bt-full py-bt mais en plus verbeux py-list Liste le code Python py-print Affiche le contenu d'une variable Python py-locals Affiche les locals py-up Remonte d'une frame py-down Descend d'une frame 85 / 107
Le code import time import os def next(i): print(f"{os.getpid()} {time.strftime('%H:%M:%S')}") time.sleep(10) i = 1 - i i = 1 while True: next(i) 86 / 107
Comment faire ? $ gdb -p 10619 GNU gdb (GDB) Fedora 8.0.1-26.fc26 ... For help, type "help". Type "apropos word" to search for commands related to "word". Attaching to process 10619 Reading symbols from /usr/bin/python3.6dm...Reading symbols from /usr/lib/debug/us Reading symbols from /lib64/libpython3.6dm.so.1.0...Reading symbols from /usr/lib/d Reading symbols from /lib64/libpthread.so.0...Reading symbols from /usr/lib/debug/ [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Reading symbols from /lib64/libdl.so.2...Reading symbols from /usr/lib/debug/usr/li Reading symbols from /lib64/libutil.so.1...Reading symbols from /usr/lib/debug/usr Reading symbols from /lib64/libm.so.6...Reading symbols from /usr/lib/debug/usr/li Reading symbols from /lib64/libc.so.6...Reading symbols from /usr/lib/debug/usr/li Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/de 0x00007f40dc696ce3 in __select_nocancel () at ../sysdeps/unix/syscall-template.S:84 84 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS) (gdb) 88 / 107
A cher le code (gdb) py-list 1 import time 2 import os 3 4 def next(i): 5 print(f"{os.getpid()} {time.strftime('%H:%M:%S')}") >6 time.sleep(10) 7 i = 1 - i 8 9 i = 1 10 while True: 11 next(i) (gdb) 89 / 107
A cher la traceback avec plus d'infos (gdb) py-bt-full #3 #7 Frame 0x55c890804748, for file ex_loop1.py, line 6, in next (i=1) time.sleep(10) #12 Frame 0x55c890804458, for file ex_loop1.py, line 11, in () next(i) 91 / 107
A cher la traceback avec plus d'infos (gdb) py-bt-full #3 #7 Frame 0x55c890804748, for file ex_loop1.py, line 6, in next (i=1) time.sleep(10) #12 Frame 0x55c890804458, for file ex_loop1.py, line 11, in () next(i) (gdb) select-frame 7 (gdb) info locals tstate = 0x55c8907a3cf0 (gdb) select-frame 3 (gdb) info locals func = 0x7f40d5974418 meth = 0x7f40dd57c6c6 self = result = 'sleep' flags = 8 __PRETTY_FUNCTION__ = "_PyCFunction_FastCallDict" 92 / 107
Examiner une frame (gdb) py-bt-full #3 #7 Frame 0x55706036f748, for file ex_loop1.py, line 6, in next (i=1) time.sleep(10) #12 Frame 0x55706036f458, for file ex_loop1.py, line 11, in () next(i) (gdb) select-frame 7 (gdb) py-list 1 import time 2 import os 3 4 def next(i): 5 print(f"{os.getpid()} {time.strftime('%H:%M:%S')}") >6 time.sleep(10) 7 i = 1 - i 8 9 i = 1 10 while True: 11 next(i) (gdb) py-print i local 'i' = 1 (gdb) py-locals i = 1 93 / 107
Navigation (gdb) py-down Unable to find a newer python frame (gdb) py-up #7 Frame 0x55c890804748, for file ex_loop1.py, line 6, in next (i=1) time.sleep(10) (gdb) py-down #3 94 / 107
Backtrace de CPython (gdb) bt #0 0x00007f40dc696ce3 in __select_nocancel () at ../sysdeps/unix/syscall-template #1 0x00007f40dd57e7f8 in pysleep (secs=10000000000) at /usr/src/debug/Python-3.6.2/Modules/timemodule.c:1438 #2 0x00007f40dd57c73d in time_sleep (self=, obj= at /usr/src/debug/Python-3.6.2/Modules/timemodule.c:235 #3 0x00007f40dd39a9af in _PyCFunction_FastCallDict ( func_obj=, args=0x55c8908048e0, nargs=1, kwargs=0x0) at /usr/src/debug/Python-3.6.2/Objects/methodobject.c:209 #4 0x00007f40dd39ada6 in _PyCFunction_FastCallKeywords ( func=, stack=0x55c8908048e0, nargs=1, kwnames=0x0) at /usr/src/debug/Python-3.6.2/Objects/methodobject.c:294 #5 0x00007f40dd4b046e in call_function (pp_stack=0x7ffd7bcab7b8, oparg=1, kwnames= at /usr/src/debug/Python-3.6.2/Python/ceval.c:4809 #6 0x00007f40dd4a8046 in _PyEval_EvalFrameDefault ( f=Frame 0x55c890804748, for file ex_loop1.py, line 6, in next (i=1), throwflag= at /usr/src/debug/Python-3.6.2/Python/ceval.c:3295 #7 0x00007f40dd494118 in PyEval_EvalFrameEx ( f=Frame 0x55c890804748, for file ex_loop1.py, line 6, in next (i=1), throwflag= at /usr/src/debug/Python-3.6.2/Python/ceval.c:718 95 / 107
Threads (gdb) info threads Id Target Id Frame 1 Thread 0x7f40ddaef700 (LWP 10619) "python3-debug" 0x00007f40dd39a9af in _PyC args=0x55c8908048e0, nargs=1, kwargs=0x0) at /usr/src/debug/Python-3.6.2/Objects/methodobject.c:209 96 / 107