10. Дебъгване

10. Дебъгване

10. Дебъгване

28 април 2014

Ако не винаги успявате от първият път

Логаритмично дебъгване

Дебъгване чрез логване

import logging
logging.basicConfig(level=logging.DEBUG)

logging.debug("Just a debug message! DON'T PANIC")
logging.info("Here, sir, have my info")
logging.critical("OMGOMGOMGOMG")

Нива на логване, по ред на страшност:

Нивото по подразбиране е WARNING

Пример

logging.basicConfig(
       level=logging.DEBUG,
       filename='debug.txt',
       format='%(asctime)s %(levelname)s: %(message)s',
       datefmt='%Y-%m-%d %H:%M:%S'
)

Използвайте logging вместо print() когато пишете пограма която е по-голяма от две прости функции, за да знаете винаги какво се случва.

Хардкор дебуболечкване

Трудно е да се дебъгва с print()/logging когато програмата забива или отказва да работи

>>> make_a_sandwich(for=me)
'NO!'
Segmentation fault (core dumped)

На помощ идват дебъгерите!

PDB

python -m pdb buggy.py
> /buggy.py(1)<module>()
-> from doomsday import Appocalypse
(Pdb) 
>>> import pdb
>>> from doomsday import Appocalypse
Traceback (most recent call last):
  File "/appocalypse.py", line 1, in <module>
  DeathAndDestruction
>>> pdb.pm() # Post mortem

PDB breakpoint

Breakpoint е място в което изпълнението на кода спира и се пуска дебъгера.

from copy import deepcopy


def bubblesort(data):
    data = deepcopy(data)
    for repeat in range(0, len(data)-1):
        index = 0
        while index < len(data) - 1:
            if data[index] > data[index + 1]:
                # Something smells fishy here
                import pdb; pdb.set_trace()
                data[index], data[index + 1] = data[index + 1], data[index]
            index += 1
    return data

PDB breakpoint 2

Добавяне на breakpoint без пипане на кода

$ python3 -m pdb python.py
> /python.py(1)<module>()
-> from food import sandwich
(Pdb) break 42
Breakpoint 1 at /python.py:42
(Pdb) break 84
Breakpoint 2 at /python.py:84

PDB команди

Изглеждат много и страшни, но половината са синоними

(Pdb) help

Documented commands (type help <topic>):
========================================
EOF    cl         disable  interact  next     return  u          where
a      clear      display  j         p        retval  unalias  
alias  commands   down     jump      pp       run     undisplay
args   condition  enable   l         print    rv      unt      
b      cont       exit     list      q        s       until    
break  continue   h        ll        quit     source  up       
bt     d          help     longlist  r        step    w        
c      debug      ignore   n         restart  tbreak  whatis

PDB команди 2

Профилиране с cProfile

cProfile.run() приема произволен питонски код като низ

import cProfile
cProfile.run("answer_to_the_question_of_life_universe_and_everything()")

import re
cProfile.run('re.compile("6x9=?|42")')

Можете да пуснете и цяла програма:

$ python -m cProfile -o <output-file-name> <script-name> <script-options>

Herr Ackermann

def ack(m, n):
    if m == 0:
        return n+1
    if n == 0:
        return ack(m-1, 1)
    return ack(m-1, ack(m, n-1))
import cProfile
cProfile.run("print(ack(3,4))")

Визуализация с RunSnakeRun

$ pip install runsnakerun
$ runsnake <cProfile dump>

Профилиране на памет

Дълго време в Питон нямаше стандартен модул за проследяване на паметта

От Python 3.4 вече има tracemalloc

import tracemalloc

tracemalloc.start()

# ... балона се надува, надувайте момчета ...

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')

# top 10 files allocating the most memory
for stat in top_stats[:10]:
    print(stat)

Кога да профилираме

Въпроси?