使用IPython逐步调试

时间:2013-05-31 23:21:05

标签: python debugging emacs ipython pdb

根据我的阅读,有两种方法可以在Python中调试代码:

  • 使用传统的调试器,例如pdbipdb。这支持c的{​​{1}},continue的{​​{1}},n的{​​{1}}等命令,但您没有直接访问IPython shell,这对于对象检查非常有用。

  • 在代码中使用embedding一个IPython shell的 IPython 。您可以执行step-over,然后在代码中使用s。当您的程序/脚本命中step-into语句时,您将被放入IPython shell中。这允许使用所有IPython好东西对对象进行全面检查并测试Python代码。但是,使用from ipython import embed时,您不能再使用便捷的键盘快捷键一步一步完成代码。

有没有办法结合两全其美?即。

  1. 能够使用方便的pdb / ipdb键盘快捷键一步一步完成代码。
  2. 在任何此类步骤(例如,在给定的声明中),可以访问完整的 IPython shell
  3. IPython调试,如 MATLAB:

    这种“增强调试”的一个例子可以在MATLAB中找到,用户总是可以完全访问MATLAB引擎/ shell,她仍然可以逐步执行-step 通过她的代码,定义条件断点等。从我与其他用户讨论的内容来看,这是人们在从MATLAB迁移到IPython时最想念的调试功能。

    在Emacs和其他编辑器中进行IPython调试:

    我不想让问题太具体,但我主要在Emacs工作,所以我想知道是否有任何方法可以将此功能纳入其中。 理想情况,Emacs(或编辑器)允许程序员在代码的任何地方设置断点,并与解释器或调试器通信,让它停在您选择的位置,并带到一个完整的IPython那个地方的翻译。

15 个答案:

答案 0 :(得分:98)

ipdb.set_trace()怎么样?在您的代码中:

import ipdb; ipdb.set_trace()

更新:现在在Python 3.7中,我们可以编写breakpoint()。它的工作原理相同,但它也遵循PYTHONBREAKPOINT环境变量。此功能来自this PEP

这允许对代码进行全面检查,并且您可以访问c(继续),n(执行下一行),s等命令(步入方法)在点)等等。

请参阅ipdb repoa list of commandsIPython现在被调用(编辑:部分)Jupyter


ps:请注意,ipdb命令优先于python代码。因此,为了撰写list(foo),您需要print list(foo)

此外,如果您喜欢ipython提示(其emacs和vim模式,历史记录,完成情况......),您的项目很容易获得相同的内容,因为它基于{{3} }。

答案 1 :(得分:40)

(2016年5月28日更新)在Emacs中使用RealGUD

对于Emacs中的任何人,this thread展示如何使用

完成OP中描述的所有内容(及更多内容)
  1. Emacs中一个名为 RealGUD 的新重要调试器,可以与任何调试器一起运行(包括ipdb)。
  2. Emacs包isend-mode
  3. 这两个软件包的组合非常强大,允许用户重新创建OP中描述的行为并做更多。

    有关Realdb的ip the wiki article的更多信息。


    原始答案:

    在尝试了许多调试Python的不同方法之后,包括这个线程中提到的所有方法,我用IPython调试Python的首选方法之一是使用嵌入式shell。

    定义自定义嵌入式IPython shell:

    在脚本上将以下内容添加到PYTHONPATH,以便方法ipsh()可用。

    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)
    

    然后,每当我想在我的代码中调试某些内容时,我将ipsh()放在我需要进行对象检查等的位置。例如,假设我想在下面调试my_function

    使用它:

    def my_function(b):
      a = b
      ipsh() # <- This will embed a full-fledged IPython interpreter
      a = 4
    

    然后我通过以下方式之一调用my_function(2)

    1. 通过运行从Unix shell调用此函数的Python程序
    2. 或直接从IPython
    3. 调用它

      无论我如何调用它,解释器都会停在显示ipsh()的行。完成后,您可以执行Ctrl-D并继续执行Python(使用您所做的任何变量更新)。请注意,如果您从常规IPython运行IPython shell(上面的案例2)中的代码,那么新的IPython shell将在您调用它的那个内部嵌套,这非常好,但是知道这件事很有意义。无论如何,一旦解释器停在ipsh的位置,我就可以检查a的值(2),看看定义了哪些函数和对象等。

      问题:

      上面的解决方案可以让Python在代码中的任何地方停止,然后让你进入一个成熟的IPython解释器。不幸的是,一旦你调用脚本,它就不允许你添加或删除断点,这非常令人沮丧。在我看来,这是阻止IPython成为Python的优秀调试工具的唯一的事情。

      您现在可以做的最好:

      解决方法是将ipsh()先验放在您希望Python解释器启动IPython shell(即breakpoint)的不同位置。然后你就可以跳过&#34;在不同的预先定义的,硬编码的&#34;断点之间&#34;使用Ctrl-D,它将退出当前嵌入的IPython shell,并在解释器遇到下一次ipsh()调用时再次停止。

      如果你走这条路,一种方法退出&#34;调试模式&#34;并忽略所有后续断点,使用ipshell.dummy_mode = True将使Python忽略我们上面创建的ipshell对象的任何后续实例化。

答案 2 :(得分:29)

尚未提及IPython的%pdb标志。只需在IPython中调用%pdb,当发生错误时,您将自动删除到ipdb。虽然您没有立即进行踩踏,但之后您将进入ipdb

这使得调试单个函数变得容易,因为您可以使用%load加载文件然后运行函数。您可以在正确位置强制出现assert错误。

答案 3 :(得分:19)

您可以从pudb启动IPython会话,然后根据需要返回调试会话。

BTW,ipdb在幕后使用IPython,你可以实际使用IPython功能,如TAB完成和魔术命令(一个以%开头)。如果您对ipdb没问题,可以使用%run%debug之类的命令从IPython启动它。 ipdb会话实际上比普通IPython更好,你可以在堆栈跟踪中上下移动等等。在ipdb中缺少“对象检查”的内容?

此外,与Emacs&gt; = 24.3捆绑在一起的python.el具有良好的ipdb支持。

答案 4 :(得分:11)

看起来像@ gaborous的答案is deprecated中的方法。

新方法似乎是:

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

def buggy_method():
    debug()

答案 5 :(得分:6)

前缀“!”您在pdb中键入命令的符号似乎与在IPython shell中执行某些操作具有相同的效果。这适用于访问某个函数甚至变量名称的帮助。也许这会在某种程度上帮助你。例如,

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

但是!help(numpy.transpose)将为您提供numpy.transpose上的预期帮助页面。类似地,对于变量名称,假设你有一个变量l,在pdb中输入“l”列出代码,但是!l打印l的值。

答案 6 :(得分:4)

您是否尝试过this tip

  

或者更好的是,使用ipython,并致电:

        $('.ppt li:gt(0)').hide();

        $('.ppt li:last').addClass('last');

        $('.ppt li:first').addClass('first');

        $('#play').hide();

            var cur = $('.ppt li:first');

            var interval;

            var time= 3000;

            function start() {

                interval = setInterval( "forward()", time );
            }

            function stop() {

                clearInterval( interval );
            }

            function forward() {

                cur.fadeOut( 0 );

            if ( cur.attr('class') == 'last' )

                cur = $('.ppt li:first');
            else

                cur = cur.next();

                cur.fadeIn( 0 );
            }

            function goFwd() {

                stop();

                forward();

                start();
            }

            function back() {

                cur.fadeOut( 0 );

            if ( cur.attr('class') == 'first' )

                cur = $('.ppt li:last');
            else

                cur = cur.prev();

                cur.fadeIn( 0 );
            }
            function goBack() {

                stop();

                back();

                start();
            }

            function showPause() {

                $('#play').hide();

                $('#stop').show();

            }

            function showPlay() {

                $('#stop').hide();

                $('#play').show();

            }

            $('#fwd').click( function() {

                goFwd();

                showPause();

            } );

            $('#back').click( function() {

                goBack();   

                showPause();

            } );

            $('#stop').click( function() {

                stop();

                showPlay();

            } );

            $('#play').click( function() {

                start();

                showPause();

            } );

            $(function() {

                start();

            } );

    </script>
     

然后你可以使用

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

每当你想设置一个断点

答案 7 :(得分:3)

一种选择是使用类似Spyder的IDE,它允许您在调试时与您的代码进行交互(实际上使用IPython控制台)。事实上,Spyder非常像MATLAB,我认为这是故意的。这包括变量检查员,变量编辑,内置文档访问等等。

答案 8 :(得分:3)

问题的正确,简单,酷,准确的答案是使用%run macro with -d flag。

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

答案 9 :(得分:2)

如果在embed()控制台中键入exit(),代码将继续并转到下一个embed()行。

答案 10 :(得分:2)

Pyzo IDE具有与OP要求相似的功能。您不必以调试模式启动。与MATLAB类似,命令在shell中执行。当您在某些源代码行中设置断点时,IDE会停止在那里执行,您也可以调试和发出常规的IPython命令。

然而,看起来步入也不会(但是?)运行良好(即停在一行然后进入另一个功能),除非你设置另一个断点。

尽管如此,来自MATLAB,这似乎是我找到的最佳解决方案。

答案 11 :(得分:1)

从Emacs的IPython-shell内部运行,并通过pdb.set_trace()设置断点应该可以。

使用python-mode.el,M-x ipython RET等进行检查。

答案 12 :(得分:1)

从python 3.2开始,你有interact命令,可以访问完整的python / ipython命令空间。

答案 13 :(得分:1)

之前没有明确说过,您可以在{em> IPython.embed()内部调用ipdb

ipdb非常适合单步执行代码和进行代码检查,但是它不能很好地处理其他事情,例如多行代码。 IPython可以很好地处理多行语句。

要引入调试器,请在代码中调用以下代码:

import idpb; ipdb.set_trace()

这可以进行自省和步进。

如果需要IPython功能,请在ipdb>中调用以下命令:

from IPython import embed; embed()

关于Emacs,这是我特有的解决方案,希望对您有所启发。

我将Emacs与M-x shell一起使用。我为yassnippet定义了一个ipdb,它会引发调试器。 bm包突出显示了该行,因此我不会忘记将其从源代码中删除:

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

我在.pdbrc文件中导入IPython:

try:
    from IPython import embed
except:
    pass

这使我可以从任何embed()实例(安装IPython时)调用ipdb

答案 14 :(得分:1)

开发新代码

在IPython中进行调试

  1. 使用Jupyter / IPython单元执行加快实验迭代速度
  2. 使用%% debug逐步完成

单元格示例:

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

调试现有代码

IPython内部调试

  1. 调试损坏的单元测试:pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. 在测试用例之外进行调试:breakpoint()python -m ipdb
  3. 在调试器中需要时提供完整IPython功能的IPython.embed()

关于Python的想法

我同意OP的观点,MATLAB可以很好地完成许多事情,Python仍然没有,而且确实应该这样做,因为该语言中的几乎所有内容都偏重于开发速度而不是生产速度。也许有一天,我将为CPython贡献一些琐碎的错误修复。

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

另请参阅Is it possible to run commands in IPython with debugging?