Iniciando o depurador python automaticamente em erro

216

Esta é uma pergunta sobre a qual me pergunto há algum tempo, mas nunca encontrei uma solução adequada. Se eu executar um script e me deparar, digamos um IndexError, o python imprime a linha, o local e a descrição rápida do erro e sai. É possível iniciar automaticamente o pdb quando um erro é encontrado? Não sou contra ter uma declaração de importação extra na parte superior do arquivo, nem algumas linhas de código extras.

Jeremy
fonte
12
Você já pensou em mudar a resposta que é aceita?
Joost

Respostas:

127

Você pode usar o traceback.print_exc para imprimir o traceback das exceções. Em seguida, use sys.exc_info para extrair o traceback e, finalmente, chame pdb.post_mortem com esse traceback

import pdb, traceback, sys

def bombs():
    a = []
    print a[0]

if __name__ == '__main__':
    try:
        bombs()
    except:
        extype, value, tb = sys.exc_info()
        traceback.print_exc()
        pdb.post_mortem(tb)

Se você deseja iniciar uma linha de comando interativa com code.interact usando os locais do quadro em que a exceção se originou, você pode executar

import traceback, sys, code

def bombs():
    a = []
    print a[0]

if __name__ == '__main__':
    try:
        bombs()
    except:
        type, value, tb = sys.exc_info()
        traceback.print_exc()
        last_frame = lambda tb=tb: last_frame(tb.tb_next) if tb.tb_next else tb
        frame = last_frame().tb_frame
        ns = dict(frame.f_globals)
        ns.update(frame.f_locals)
        code.interact(local=ns)
Florian Bösch
fonte
a primeira solução é mais discutido no livro de receitas pitão
dirkjot
3
por que alguém preferiria codemais, pdbjá que o último parece expandir-se?
K3 --- rnc
Eu tenho a mesma pergunta? Por que você prefere code?
ARH
2
Em seguida, use sys.exc_infopara extrair o retorno e, finalmente, ligue pdb.post_mortemcom esse retorno . Você não precisa passar o objeto de rastreamento para pdb.post_mortem. Da documentação : se nenhum retorno for fornecido, ele usará a exceção que está sendo tratada no momento (uma exceção deve ser tratada para que o padrão seja usado).
Piotr Dobrogost
2
@PiotrDobrogost Good point. Eu acho que é mais útil saber que você pode passar um objeto tb, pois ele demonstra melhor a API. É bom saber que as duas opções existem.
DavidA
453
python -m pdb -c continue myscript.py

Se você não fornecer o -c continuesinalizador, precisará digitar 'c' (para Continuar) quando a execução começar. Em seguida, ele será executado no ponto de erro e lhe dará o controle lá. Conforme mencionado por eqzx , esse sinalizador é uma nova adição no python 3.2, portanto é necessário inserir 'c' para versões anteriores do Python (consulte https://docs.python.org/3/library/pdb.html ).

Catherine Devlin
fonte
5
Obrigado por mencionar o " enter 'c' " - eu costumava inserir 'r' (para "run"), sendo usado a partir dele gdb; e quando você digita 'r' pdb, o programa realmente é executado, mas NÃO para (nem gera backtrace) por erro; fiquei confuso até ler isso. Felicidades!
Sdaau
3
Vineet, ele iniciará você com o depurador ativado, digite "cont" e será executado até que o erro seja encontrado. A partir daí, você pode inspecionar variáveis, etc., como em qualquer outra sessão pdb.
Catherine Devlin
40
Por favor, OP, aceite isso como resposta. Este é o mais útil e eu perdi 5 minutos lendo os outros até eu acertar este ... este deve ser o primeiro!
Jhegedus
4
Isso também funciona da mesma maneira com ipdb; e, é claro, argumentos podem ser adicionados após o script!
tutuDajuju
20
Isso não funciona com o Python 2.7. docs.python.org/3/library/pdb.html : "Novo na versão 3.2: o pdb.py agora aceita uma opção -c que executa comandos"
eqzx 10/04/19
68

Use o seguinte módulo:

import sys

def info(type, value, tb):
    if hasattr(sys, 'ps1') or not sys.stderr.isatty():
    # we are in interactive mode or we don't have a tty-like
    # device, so we call the default hook
        sys.__excepthook__(type, value, tb)
    else:
        import traceback, pdb
        # we are NOT in interactive mode, print the exception...
        traceback.print_exception(type, value, tb)
        print
        # ...then start the debugger in post-mortem mode.
        # pdb.pm() # deprecated
        pdb.post_mortem(tb) # more "modern"

sys.excepthook = info

Nomeie debug(ou o que você quiser) e coloque-o em algum lugar do seu caminho python

Agora, no início do seu script, basta adicionar um import debug.

tzot
fonte
2
Essa deve ser a resposta aceita - não requer nenhuma modificação do código existente ou envolve tudo em um try-catchIMO feio.
Csphar 7/09
Eu amo esta resposta bastante frequência, mas prefere pudbmais pdb. Continue voltando para copiar e colar, o que certamente diz algo sobre a falta de ordem na minha vida.
Stabledog
47

O Ipython possui um comando para alternar esse comportamento: % pdb . Ele faz exatamente o que você descreveu, talvez um pouco mais (fornecendo backtraces mais informativos com destaque de sintaxe e conclusão de código). Definitivamente vale a pena tentar!

Latanius
fonte
3
E essa é a única resposta razoável para isso.
Michael
4
Observe que - como também observado nos documentos @matthiash linked - %debugpermite abrir o depurador após encontrar um erro. Eu geralmente prefiro isso %pdb. (O trade-off é apenas digitando qcada vez que você não quer depurar um vs. erro de digitação %debugcada vez que você fazer deseja depurar um erro.)
Braham Snyder
1
Observe também que a configuração c.InteractiveShell.pdb = Trueé ipython_config.pyativada %pdbautomaticamente para cada sessão.
Braham Snyder
33

Este não é o depurador, mas provavelmente tão útil (?)

Eu sei que ouvi Guido mencionar isso em um discurso em algum lugar.

Acabei de verificar python -? E, se você usar o comando -i, poderá interagir onde seu script parou.

Então, dado este script:

testlist = [1,2,3,4,5, 0]

prev_i = None
for i in testlist:
    if not prev_i:
        prev_i = i
    else:
        result = prev_i/i

Você pode obter essa saída!

PS D:\> python -i debugtest.py
Traceback (most recent call last):
  File "debugtest.py", line 10, in <module>
    result = prev_i/i
ZeroDivisionError: integer division or modulo by zero
>>>
>>>
>>> prev_i
1
>>> i
0
>>>

Para ser sincero, não usei isso, mas deveria ser, parece muito útil.

monkut
fonte
Leve, mas muitas vezes apenas o que é necessário
Casebash
8
Não é tão útil quanto, lança-se no escopo global. Não é possível mexer em qualquer função que tenha travado.
pixelpax
21

O IPython simplifica isso na linha de comando:

python myscript.py arg1 arg2

pode ser reescrito para

ipython --pdb myscript.py -- arg1 arg2

Ou, da mesma forma, se estiver chamando um módulo:

python -m mymodule arg1 arg2

pode ser reescrito para

ipython --pdb -m mymodule -- arg1 arg2

Observe o --para impedir que o IPython leia os argumentos do script como seus.

Isso também tem a vantagem de chamar o depurador IPython aprimorado (ipdb) em vez do pdb.

wodow
fonte
9

Se você estiver usando ipython, após o lançamento, digite%pdb

In [1]: %pdb
Automatic pdb calling has been turned ON
Willemoes
fonte
Jupyter por exemplo
Blaztix
6

Se você estiver usando o ambiente IPython, basta usar o% debug e o shell o levará de volta à linha ofensiva com o ambiente ipdb para inspeções etc. Outra opção, conforme indicado acima, é usar o magic ipdthon% pdb o mesmo.

Jehandad
fonte
Observe que, se o erro ocorreu em uma função do módulo, você pode navegar pelos quadros com upe downcomandos para retornar à linha do seu código que gerou o erro.
Jean Paul
4

Você pode colocar esta linha no seu código:

import pdb ; pdb.set_trace()

Mais informações: Inicie o depurador python em qualquer linha

Rory
fonte
8
Isso interrompe o código e inicia um depurador na linha onde você coloca este comando, não na linha onde ocorreu uma exceção
blueFast
3

Para executá-lo sem precisar digitar c no início, use:

python -m pdb -c c <script name>

O Pdb possui seus próprios argumentos de linha de comando: -cc executará o comando c (ontinue) no início da execução e o programa será executado ininterruptamente até o erro.

Zlatko Karakaš
fonte
3

python -m pdb script.py em python2.7, pressione continue para iniciar e ele será executado com o erro e será interrompido para depuração.

Hannah
fonte
1

Se você estiver executando um módulo:

python -m mymodule

E agora você deseja inserir pdbquando ocorrer uma exceção, faça o seguinte:

PYTHONPATH="." python -m pdb -c c mymodule/__main__.py

(ou estenda sua PYTHONPATH). O PYTHONPATHé necessário para que o módulo é encontrado no caminho, uma vez que você estiver executando o pdbmódulo agora.

blueFast
fonte
0

Coloque um ponto de interrupção dentro do construtor da classe de exceção mais alta na hierarquia e, na maioria das vezes, você verá onde o erro foi gerado.

Colocar um ponto de interrupção significa o que você quiser: você pode usar um IDE pdb.set_traceou

vlad-ardelean
fonte