Written by: Helio Loureiro
Category: Python
Hits: 10745

 Vou descrever aqui mais uma dica de uso que um processo ou ferramenta.  Como faz vários anos que programo em python, em certo ponto achei razoável adicionar uma variável e parâmetro pra debug.  Então todos meu programas em python em geral tem uma estrutura mais ou menos assim:

#! /usr/bin/python

def debug(msg):
print "DEBUG: %s" % msg

class MinhaClasse:
código
código
código

if __name__ == '__main__':
o = MinhaClasse()
o.main()

Então o que faço em geral é ter uma função debug(), mesmo que use classe e orientação à objetos, pra facilitar a chamada.  Porque eu uso a função?  Se eu usar como método dentro da classe, tem de chamar toda vez como self.debug().  Como não vejo muita vantagem nisso, prefiro definir sempre como função no topo do código.

Mas esse é um exemplo pra mostrar o princípio.  O que uso é um pouco mais elaborado que isso.  Vamos melhorar esse código pra entender melhor criando alguns métodos como __init__() e main().

#! /usr/bin/python

import getopt

DEBUG = False
def debug(msg):
if DEBUG:
print "DEBUG: %s" % msg

class MinhaClasse:
def __init__(self):
debug("Construtor da classe")

def fazalgo(self):
debug("Fazendo algo")

def main(self):
debug("Chamando main")
self.fazalgo()


if __name__ == '__main__':
try: opts, args = getopt.getopt(sys.argv[1:], "d") for opt, arg in opts: if opt == "-d": DEBUG = True debug("DEBUG ENABLED") except getopt.GetoptError: pass
if os.environ.has_key("DEBUG"): DEBUG = True
o = MinhaClasse()
o.main()

Primeiramente eu adicione algum tipo de verificação de opção.  Pode ser com getopt como argparse.  Como é uma opção simples, pra verificar o parâmetro "-d", de debug ativo, usei getopt.  Em seguida usei uma variável global DEBUG, que fica como padrão em False, ou seja, desligado.

Quando faço a chamada na parte de baixo, onde __name__ é '__main__', verifico a opção via flag ou via variável de shell.  Isso quer dizer que se eu usar o script de 2 formas, terei debug ativado:

> ./meuscript.py -d

ou

> env DEBUG=1 ./meuscript.py

a segunda forma ajuda no caso de ter um sistema mais complexo e um shell script chamar seu programa.  Daí se vários scripts verificarem as variáveis de shell pra buscar por DEBUG (ou $DEBUG), fica fácil ativar/desativar.

E assim até hoje eu debugo meus programas.  Claro que fiz alguns aperfeiçoamentos como uma função que imprime o nome do método que está rodando.

def __funcname__(depth=0):
    """
    To print function names like __func__ in C.
    """
    return "__function__: " + sys._getframe(depth + 1).f_code.co_name + "()"

Assim, dentro de um método, posso usar debug da seguinte forma:

class MinhaClasse:
def __init__(self):
debug(__funcname__)

E isso ajuda ao imprimir o nome da função corrente, da mesma forma que se usa a macro__FUNCTION__ em C. Essa dica eu achei recentement no StackOverflow:
http://stackoverflow.com/questions/5067604/determine-function-name-from-within-that-function-without-using-traceback
E por último, e acabei com o tempo refinando meu debug().  Ao invés de de somente aceitar string, eu fiz um seletor de tipo pra imprimir qualquer variável, inclusive dicionários no formato json pra ficar mais fácil ler.

#! /usr/bin/python
import json
import getopt

DEBUG = False

def debug(msg):
    """ Debug helper """
    if DEBUG:
        if type(msg) == type("abc"):
            # it is ok
            None
        elif type(msg) == type({}):
            msg = "%s" % json.dumps(msg, indent=4)
        elif type(msg) == type([]):
            msg = "[ %s ]" % ", ".join(msg)
        msg = "DEBUG(%s): %s" % (__file__, msg)
        print msg
        syslog.syslog(syslog.LOG_DEBUG, msg)

def __funcname__(depth=0):
    """
    To print function names like __func__ in C.
    """
    return "__function__: " + sys._getframe(depth + 1).f_code.co_name + "()"

class MinhaClasse:
    def __init__(self):
        debug(__funcname__)
        debug("Construtor da classe")
        self.nome = "Helio"
        self.sobrenome = "Loureiro"
        debug("Nome: %s" % self.nome)
        debug("Sobrenome: %s" % self.sobrenome)
        self.dados = {}

    def fazalgo(self):
        debug(__funcname__)
        debug("Fazendo algo")
        for k, v in self.dados.items():
            debug("%s => %s" % (k, v) )

    def main(self):
        debug(__funcname__)
        self.dados = { "Nome" : self.nome,
                       "Sobrenome" : self.sobrenome }
        debug(self.dados)
        self.fazalgo()

if __name__ == '__main__':
    try: 
        opts, args = getopt.getopt(sys.argv[1:], "d") 
        for opt, arg in opts: 
            if opt == "-d": 
                DEBUG = True 
                debug("DEBUG ENABLED") 
    except getopt.GetoptError: 
        pass 
    if os.environ.has_key("DEBUG"): 
        DEBUG = True 

    o = MinhaClasse()
    o.main()

Como escrevi anteriormente, não é um padrão fazer isso.  Existem módulos que ajudam a debuggar de forma até mais profunda.  Eu gosto de escrever minhas mensagens de debug pra filtrar melhor as mensagens e poder ver o que realmente importa.  Então fica aqui a dica.