Depuração passo a passo com IPython


170

Pelo que li, há duas maneiras de depurar código no Python:

  • Com um depurador tradicional como pdbou ipdb. Isso suporta comandos como cfor continue, nfor step-over, sfor step-intoetc.), mas você não tem acesso direto a um shell IPython que pode ser extremamente útil para inspeção de objetos.

  • Usando o IPython , incorporando um shell IPython ao seu código. Você pode fazer from ipython import embede usar embed()no seu código. Quando seu programa / script atinge uma embed()instrução, você é colocado em um shell IPython. Isso permite a inspeção completa dos objetos e o teste do código Python usando todas as vantagens do IPython. No entanto, ao usar, embed()você não pode mais passo a passo através do código com atalhos de teclado úteis.

Existe alguma maneira de combinar o melhor dos dois mundos? Ou seja,

  1. Veja passo a passo o seu código com atalhos de teclado pdb / ipdb úteis.
  2. Em qualquer uma dessas etapas (por exemplo, em uma determinada instrução), tenha acesso a um shell IPython completo .

Depuração de IPython como no MATLAB:

Um exemplo desse tipo de "depuração aprimorada" pode ser encontrado no MATLAB, onde o usuário sempre tem acesso total ao mecanismo / shell MATLAB e ainda pode passo a passo através de seu código, definir pontos de interrupção condicionais etc. o que discuti com outros usuários, esse é o recurso de depuração que as pessoas mais sentem falta ao passar do MATLAB para o IPython.

Depuração de IPython no Emacs e em outros editores:

Não quero tornar a pergunta muito específica, mas trabalho principalmente no Emacs, por isso me pergunto se existe alguma maneira de trazer essa funcionalidade para ele. Idealmente , o Emacs (ou o editor) permitiria ao programador definir pontos de interrupção em qualquer lugar do código e se comunicar com o intérprete ou depurador para que ele parasse no local de sua escolha e traga para um intérprete IPython completo nesse local.


pdb tem !comando que executa qualquer comando python no ponto de interrupção
Dmitry Galchinsky

5
Também estou procurando um depurador python semelhante ao Matlab! Por exemplo, eu faço muita prototipagem no shell python. Todas as variáveis ​​são salvas com o shell. Agora eu encontro um problema. Espero depurar um pequeno pedaço de código, com essas variáveis ​​calculadas com o shell. No entanto, um novo depurador não pode acessar variáveis ​​antigas. Não é conveniente para prototipagem.
user1914692

1
Para usuários do Emacs, o RealGUD possui uma interface incrivelmente boa.
Clément

1
Thanks @ Clément Estive acompanhando o repo no mês passado e estou muito empolgado com o projeto :) Ainda não o testei, mas uma vez que eu (ou se você o fizer), sinta-se à vontade para escrever uma resposta aqui que talvez mostra como realizar o que é solicitado. Para outras referências, o URL é github.com/rocky/emacs-dbgr
Amelio Vazquez-Reina

@ Clément Além disso, se você tem alguma experiência com o RealGUD e o ipdb, tentei usá-lo como explicado aqui github.com/rocky/emacs-dbgr/issues/96 sem sorte.
Amelio Vazquez-Reina

Respostas:


61

Você pode usar a %pdbmagia do IPython . Basta chamar o %pdbIPython e, quando ocorrer um erro, você será automaticamente descartado ipdb. Enquanto você não tem o passo imediato, você entra ipdbdepois.

Isso facilita a depuração de funções individuais, pois você pode carregar um arquivo %loade executar uma função. Você pode forçar um erro com um assertna posição correta.

%pdbé uma mágica de linha. Chamá-lo como %pdb on, %pdb 1, %pdb offou %pdb 0. Se chamado sem argumento, funciona como uma alternância.


6
^ Esta é a verdadeira resposta
Cesar

Existe algo semelhante em que o pdb tem funcionalidade semelhante ao IPython sem estar inicialmente no IPython?
Lucas

4
Você também pode iniciar o programa ipython --pdb file.py -- argse cair para o ipdb em uma exceção. Pode valer a pena adicionar à resposta.
sebastian

110

E o ipdb.set_trace ()? No seu código:

import ipdb; ipdb.set_trace()

update : agora em Python 3.7, podemos escrever breakpoint(). Funciona da mesma forma, mas também obedece à PYTHONBREAKPOINTvariável de ambiente. Esse recurso vem deste PEP .

Isso permite a inspeção completa do seu código, e você tem acesso a comandos como c(continuar), n(executar a próxima linha), s(entrar no método no ponto) e assim por diante.

Veja o repo ipdb e uma lista de comandos . O IPython agora é chamado (edit: parte de) Jupyter .


ps: observe que um comando ipdb tem precedência sobre o código python. Então, para escrever list(foo)você precisaria print list(foo).

Além disso, se você gosta do prompt do ipython (seus modos emacs e vim, histórico, conclusões ...), é fácil obter o mesmo para o seu projeto, pois ele é baseado no kit de ferramentas do prompt do python .


10
É assim que se faz.
precisa saber é o seguinte

Minha resposta agora inclui como usar ipdbno Emacs usando RealGUDe isend-modefazer exatamente o que o OP pede.
Amelio Vazquez-Reina

1
O Jupyter não substitui o IPython, mas o IPython Notebook. Os notebooks Jupyter usam o kernel em segundo plano. Para notebooks Python, o kernel geralmente é um kernel IPython. O projeto IPython continua ainda mais.
FA

1
breakpoint()é maravilhoso. No PyCharm, você ainda o coloca no depurador PyCharm. Também é uma maneira rápida de inserir o depurador PyCharm a partir de funções que foram coladas no console.
Richard Möhn 5/12/19

40

(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

  1. um novo depurador importante no Emacs chamado RealGUD, que pode operar com qualquer depurador (inclusive ipdb).
  2. 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_functionabaixo

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:

  1. Executando um programa Python que chama essa função a partir de um shell Unix
  2. 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-De 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 = Trueque fará o Python ignorar quaisquer instâncias subsequentes do ipshellobjeto que criamos acima.


2
Atualize isso sempre que encontrar uma solução ainda melhor. Mas isso parece ótimo. Eu vou usá-lo.
Phani

1
Onde / como você define / importa cfg, banner_msg, exit_msg e inspeciona?
Gordon Feijão

1
Isso parece funcionar bem para mim: github.com/kdodia/snippets/blob/master/ipsh.py
karan.dodia

1
Eu precisava adicionar import inspectantes que funcionasse. A personalização da linha de comando parece estar corrompida para mim.
Pascal

7
Por que não usar apenas import ipdb; ipdb.set_trace()? Talvez eu sinta falta de algo, mas não vejo a vantagem do seu método muito mais complicado.
luator

19

Você pode iniciar a sessão do IPython a partir do pudb e voltar à sessão de depuração como desejar.

BTW, o ipdb está usando o IPython nos bastidores e você pode realmente usar a funcionalidade do IPython, como a conclusão do TAB e os comandos mágicos (aquele começa com %). Se você concorda com o ipdb, pode iniciá-lo no IPython usando comandos como %rune %debug. A sessão do ipdb é realmente melhor que a do IPython comum, no sentido em que você pode subir e descer no rastreamento de pilha, etc. O que está faltando no ipdb para "inspeção de objeto"?

Além disso, o python.el empacotado com o Emacs> = 24.3 possui um bom suporte ao ipdb.


1
Obrigado tkf. Eu sou um grande fã do seu pacote Emacs-Jedi. Quando você disse que o Emacs 24.3 tem um bom suporte para ipdb, você se importaria de elaborar? Normalmente, inicio o IPython em um M-x ansi-termbuffer separado e, em seguida, uso o modo isend para vincular meus buffers de origem ao buffer do IPython, para que eu possa enviar código ao intérprete do IPython com um atalho de teclado que envia automaticamente a %pastemágica para o buffer do IPython. Isso me permite testar rapidamente regiões no IPython. Eu sempre executo meus programas nesse shell IPython com rune uso embed()para parar.
Amelio Vazquez-Reina

3
Por exemplo, quando você percorre o código, o código-fonte é aberto no outro buffer com a seta apontando para o ponto de execução atual. Você também tem o comando send-region, como no modo isend.
Tkf

Thanks @tkf Como posso iniciar uma ipdbsessão de depuração usando python.ele ter coisas como send-regionetc. no shell correspondente? Em geral, onde posso encontrar mais informações sobre isso?
Amelio Vazquez-Reina

Eu uso %runou %debug. Eu acho que import ipdb; ipdb.set_trace()funciona também. Eu não acho que você pode enviar várias linhas para o ipdb. Essa é a limitação do ipdb. Mas provavelmente o %pastetruque funciona. Você pode enviar uma solicitação de recurso para o IPython dev.
tkf

13

Parece que a abordagem na resposta de @ gaborous está obsoleta .

A nova abordagem parece ser:

from IPython.core import debugger
debug = debugger.Pdb().set_trace

def buggy_method():
    debug()

Isso permite que você use ipdbinstruções no shell do ipython durante a depuração?
Alpha_989 01/01

6

Prefixando um "!" O símbolo dos comandos digitados em pdb parece ter o mesmo efeito que fazer algo em um shell IPython. Isso funciona para acessar a ajuda de uma determinada função ou até nomes de variáveis. Talvez isso ajude você até certo ponto. Por exemplo,

ipdb> help(numpy.transpose)
*** No help on (numpy.transpose)

Mas! Help (numpy.transpose) fornece a página de ajuda esperada em numpy.transpose. Da mesma forma, para nomes de variáveis, digamos que você tenha uma variável l, digitar "l" no pdb lista o código, mas! L imprime o valor de l.


2
Esta é uma pegadinha pdb desagradável e vale a pena conhecer.
Wilfred Hughes

4

Você tentou esta dica ?

Ou melhor ainda, use ipython e chame:

from IPython.Debugger import Tracer; debug_here = Tracer()

então você pode apenas usar

debug_here()

sempre que você quiser definir um ponto de interrupção


4

Você pode começar IPython de dentro ipdb !

Induza o ipdbdepurador 1 :

import idpb; ipdb.set_trace()

Digite IPython de dentro no ipdb>console 2 :

from IPython import embed; embed()

Retorne ao ipdb>console de dentro IPython:

exit

Se você tiver a sorte de usar o Emacs, as coisas podem ser ainda mais convenientes!

Isso requer o uso M-x shell. Usando yasnippete bm , defina o seguinte snippet. Isso substituirá o texto ipdbno editor pela set-tracelinha. Após inserir o trecho, a linha será destacada para que seja facilmente perceptível e navegável. Use M-x bm-nextpara navegar.

# -*- mode: snippet -*-
# name: ipdb
# key: ipdb
# expand-env: ((yas-after-exit-snippet-hook #'bm-toggle))
# --
import ipdb; ipdb.set_trace()

1 Tudo em uma linha para exclusão fácil. Como importssó acontece uma vez, este formulário garante ipdbque será importado quando você precisar, sem custos adicionais.

2 Você pode economizar digitando importando IPython para o seu .pdbrcarquivo :

try:
    from IPython import embed
except:
    pass

Isso permite que você simplesmente chame embed()de dentro ipdb(é claro, somente quando o IPython estiver instalado).


3

Uma opção é usar um IDE como o Spyder, que deve permitir que você interaja com seu código durante a depuração (usando um console IPython, de fato). Na verdade, o Spyder é muito parecido com o MATLAB, que eu presumo que foi intencional. Isso inclui inspetores variáveis, edição de variáveis, acesso interno à documentação, etc.


3

a resposta certa, fácil, legal e exata para a pergunta é usar% run macro com o sinal -d.

In [4]: run -d myscript.py
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.        
> /cygdrive/c/Users/mycodefolder/myscript.py(4)<module>()
      2                                                            
      3                        
----> 4 a=1                                            
      5 b=2

Isso deve ser mais alto. Se eu precisar inserir uma linha no meu código (dentro de um módulo), preciso reiniciar o IPython para recarregar o módulo. Com isso, eu posso apenas %save /tmp/foo.py x-yO código que eu quero executar e depurar e, em seguida, %run -d /tmp/foo.pydefinir o ponto de interrupção onde eu quiser. Ótima resposta!
Romeo Valentin

2

Se você digitar exit () no console embed (), o código continuará e vá para a próxima linha embed ().


2

O Pyzo IDE possui recursos semelhantes aos solicitados pelo OP. Você não precisa iniciar no modo de depuração. Da mesma forma que o MATLAB, os comandos são executados no shell. Quando você configura um ponto de interrupção em alguma linha de código-fonte, o IDE interrompe a execução e você pode depurar e emitir comandos regulares do IPython.

Parece, no entanto, que a intervenção não funciona (ainda?) (Por exemplo, parar em uma linha e depois entrar em outra função), a menos que você configure outro ponto de interrupção.

Ainda assim, vindo do MATLAB, esta parece a melhor solução que encontrei.


2

No python 3.2, você tem o interactcomando, que fornece acesso ao espaço de comando completo do python / ipython.


1

Executar a partir do IPython-shell e do ponto de interrupção do Emacs, via pdb.set_trace (), deve funcionar.

Verificado com python-mode.el, Mx ipython RET etc.


1

Desenvolvendo novo código

Depurando dentro do IPython

  1. Use a execução de célula Jupyter / IPython para acelerar iterações de experimentos
  2. Use %% debug para avançar

Exemplo de célula:

%%debug
...: for n in range(4):
...:    n>2

Depurando código existente

IPython dentro da depuração

  1. Depurando um teste de unidade quebrado: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. Depuração do lado de fora do caso de teste: breakpoint(), python -m ipdb, etc.
  3. IPython.embed () para funcionalidade completa do IPython, quando necessário, enquanto estiver no depurador

Pensamentos sobre Python

Concordo com o OP de que muitas coisas que o MATLAB faz muito bem, o Python ainda não tem e realmente deveria, uma vez que praticamente tudo na linguagem favorece a velocidade do desenvolvimento em detrimento da velocidade da produção. Talvez um dia eu contribua com mais do que correções triviais para o CPython.

https://github.com/ipython/ipython/commit/f042f3fea7560afcb518a1940daa46a72fbcfa68

Consulte também É possível executar comandos no IPython com depuração?

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.