(Atualização em 28 de maio de 2016) Usando o RealGUD no Emacs
Para qualquer pessoa no Emacs, este tópico mostra como realizar tudo descrito no OP (e mais) usando
- um novo depurador importante no Emacs chamado RealGUD, que pode operar com qualquer depurador (inclusive
ipdb
).
- O pacote Emacs
isend-mode
.
A combinação desses dois pacotes é extremamente poderosa e permite recriar exatamente o comportamento descrito no OP e fazer ainda mais.
Mais informações no artigo wiki do RealGUD para ipdb.
Resposta original:
Depois de ter tentado muitos métodos diferentes para depurar o Python, incluindo tudo mencionado neste segmento, uma das minhas maneiras preferidas de depurar o Python com o IPython é com shells incorporados.
Definindo um shell IPython incorporado personalizado:
Adicione o seguinte em um script ao seu PYTHONPATH
, para que o método ipsh()
fique disponível.
import inspect
# First import the embed function
from IPython.terminal.embed import InteractiveShellEmbed
from IPython.config.loader import Config
# Configure the prompt so that I know I am in a nested (embedded) shell
cfg = Config()
prompt_config = cfg.PromptManager
prompt_config.in_template = 'N.In <\\#>: '
prompt_config.in2_template = ' .\\D.: '
prompt_config.out_template = 'N.Out<\\#>: '
# Messages displayed when I drop into and exit the shell.
banner_msg = ("\n**Nested Interpreter:\n"
"Hit Ctrl-D to exit interpreter and continue program.\n"
"Note that if you use %kill_embedded, you can fully deactivate\n"
"This embedded instance so it will never turn on again")
exit_msg = '**Leaving Nested interpreter'
# Wrap it in a function that gives me more context:
def ipsh():
ipshell = InteractiveShellEmbed(config=cfg, banner1=banner_msg, exit_msg=exit_msg)
frame = inspect.currentframe().f_back
msg = 'Stopped at {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)
# Go back one level!
# This is needed because the call to ipshell is inside the function ipsh()
ipshell(msg,stack_depth=2)
Então, sempre que eu quiser depurar algo no meu código, coloco ipsh()
no local onde preciso fazer a inspeção de objetos, etc. Por exemplo, digamos que quero depurar my_function
abaixo
Usando isso:
def my_function(b):
a = b
ipsh() # <- This will embed a full-fledged IPython interpreter
a = 4
e depois invoco my_function(2)
de uma das seguintes maneiras:
- Executando um programa Python que chama essa função a partir de um shell Unix
- Ou invocando-o diretamente do IPython
Independentemente de como eu o invoque, o intérprete para na linha que diz ipsh()
. Quando terminar, você pode fazer Ctrl-D
e o Python retomará a execução (com todas as atualizações de variáveis que você fez). Observe que, se você executar o código a partir de um IPython comum, o shell IPython (caso 2 acima), o novo shell IPython será aninhado dentro daquele em que você o chamou, o que é perfeitamente aceitável, mas é bom estar ciente disso. De qualquer forma, uma vez que o intérprete para na localização de ipsh
, posso inspecionar o valor de a
(qual é 2
), ver quais funções e objetos são definidos etc.
O problema:
A solução acima pode ser usada para que o Python pare em qualquer lugar que você queira no seu código e, em seguida, coloque você em um intérprete IPython completo. Infelizmente, ele não permite adicionar ou remover pontos de interrupção depois de chamar o script, o que é altamente frustrante. Na minha opinião, isso é a única coisa que impede o IPython de se tornar uma ótima ferramenta de depuração para o Python.
O melhor que você pode fazer por enquanto:
Uma solução alternativa é colocar ipsh()
a priori nos diferentes locais em que você deseja que o interpretador Python inicie um shell IPython (ou seja, a breakpoint
). Você pode "pular" entre diferentes "pontos de interrupção" predefinidos e codificados Ctrl-D
, que sairiam do shell IPython incorporado atual e parariam novamente sempre que o intérprete atingir a próxima chamada ipsh()
.
Se você seguir esta rota, uma maneira de sair do "modo de depuração" e ignorar todos os pontos de interrupção subseqüentes é usar o ipshell.dummy_mode = True
que fará o Python ignorar quaisquer instâncias subsequentes do ipshell
objeto que criamos acima.
!
comando que executa qualquer comando python no ponto de interrupção