问题:使用IPython进行分步调试

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

  • 使用传统的调试器,例如pdbipdb。它支持诸如cfor continuenfor step-oversfor step-into等命令,但是您没有直接访问IPython shell的权限,这对于对象检查非常有用。

  • 通过 IPython shell 嵌入代码中来使用 IPython。您可以这样做,然后在您的代码中使用。当您的程序/脚本命中一条语句时,您将进入IPython shell。这允许使用所有IPython好东西对对象进行全面检查并测试Python代码。但是,在使用时,您将无法通过便捷的键盘快捷键逐步完成代码。from ipython import embedembed()embed()embed()

有什么办法可以融合两全其美?即

  1. 能够 使用方便的pdb / ipdb键盘快捷键逐步完成代码。
  2. 在任何这样的步骤(例如,在给定的语句上),都可以访问成熟的IPython shell

如在 MATLAB中一样进行 IPython调试:

在MATLAB中可以找到这种“增强调试”类型的示例,在该示例中,用户始终可以完全访问MATLAB引擎/外壳,并且她仍然可以逐步完成代码,定义条件断点等。我已经与其他用户讨论过,这是人们从MATLAB转移到IPython时最想念的调试功能。

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

我不想让这个问题过于具体,但是我主要在Emacs中工作,所以我想知道是否有任何方法可以将此功能引入其中。理想情况下,Emacs(或编辑器)将允许程序员在代码上的任意位置设置断点,并与解释器或调试器进行通信,以使其在您选择的位置停止,并在该位置提供完整的IPython解释器。

From what I have read, there are two ways to debug code in Python:

  • With a traditional debugger such as pdb or ipdb. This supports commands such as c for continue, n for step-over, s for step-into etc.), but you don’t have direct access to an IPython shell which can be extremely useful for object inspection.

  • Using IPython by embedding an IPython shell in your code. You can do from IPython import embed, and then use embed() in your code. When your program/script hits an embed() statement, you are dropped into an IPython shell. This allows the full inspection of objects and testing of Python code using all the IPython goodies. However, when using embed() you can’t step-by-step through the code anymore with handy keyboard shortcuts.

Is there any way to combine the best of both worlds? I.e.

  1. Be able to step-by-step through your code with handy pdb/ipdb keyboard shortcuts.
  2. At any such step (e.g. on a given statement), have access to a full-fledged IPython shell.

IPython debugging as in MATLAB:

An example of this type of “enhanced debugging” can be found in MATLAB, where the user always has full access to the MATLAB engine/shell, and she can still step-by-step through her code, define conditional breakpoints, etc. From what I have discussed with other users, this is the debugging feature that people miss the most when moving from MATLAB to IPython.

IPython debugging in Emacs and other editors:

I don’t want to make the question too specific, but I work mostly in Emacs, so I wonder if there is any way to bring this functionality into it. Ideally, Emacs (or the editor) would allow the programmer to set breakpoints anywhere on the code and communicate with the interpreter or debugger to have it stop in the location of your choice, and bring to a full IPython interpreter on that location.


回答 0

您可以使用IPython的。只需调用%pdbIPython,当发生错误时,您会自动转到ipdb。虽然您没有立即迈出一步,但您ipdb之后就进入了。

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

%pdb是线魔术。呼之为%pdb on%pdb 1%pdb off%pdb 0。如果在不带参数的情况下调用它,则它将作为切换。

You can use IPython’s . Just call %pdb in IPython and when an error occurs, you’re automatically dropped to ipdb. While you don’t have the stepping immediately, you’re in ipdb afterwards.

This makes debugging individual functions easy, as you can just load a file with %load and then run a function. You could force an error with an assert at the right position.

%pdb is a line magic. Call it as %pdb on, %pdb 1, %pdb off or %pdb 0. If called without argument it works as a toggle.


回答 1

ipdb.set_trace()呢?在您的代码中:

import ipdb; ipdb.set_trace()

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

这样可以对代码进行全面检查,并且您可以访问诸如c(继续),n(执行下一行),s(进入当前方法)之类的命令。

请参阅ipdb repo命令列表IPython现在称为Jupyter(的一部分)。


ps:请注意,ipdb命令优先于python代码。所以为了写,list(foo)你需要print list(foo)

另外,如果您喜欢ipython提示符(它的emacs和vim模式,历史记录,完成情况等),由于它基于python提示符工具包,因此很容易为您的项目获得相同的名称。

What about ipdb.set_trace() ? In your code :

import ipdb; ipdb.set_trace()

update: now in Python 3.7, we can write breakpoint(). It works the same, but it also obeys to the PYTHONBREAKPOINT environment variable. This feature comes from this PEP.

This allows for full inspection of your code, and you have access to commands such as c (continue), n (execute next line), s (step into the method at point) and so on.

See the ipdb repo and a list of commands. IPython is now called (edit: part of) Jupyter.


ps: note that an ipdb command takes precedence over python code. So in order to write list(foo) you’d need print(list(foo)), or !list(foo) .

Also, if you like the ipython prompt (its emacs and vim modes, history, completions,…) it’s easy to get the same for your project since it’s based on the python prompt toolkit.


回答 2

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

对于Emacs中的任何人,此线程都说明了如何使用

  1. Emacs中一个称为RealGUD的新的重要调试器,可以与任何调试器(包括ipdb)一起使用。
  2. Emacs软件包isend-mode

这两个软件包的组合非常强大,可以使它们完全重新创建OP中描述的行为,甚至可以做更多的事情。

有关RealGUD for ipdb 的Wiki文章的更多信息。


原始答案:

在尝试了多种不同的调试Python方法(包括本线程中提到的所有内容)之后,我使用IPython调试Python的首选方法之一是使用嵌入式外壳程序。

定义定制的嵌入式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调用

不管我如何调用它,解释器都会停在所说的行ipsh()。完成后,您可以执行操作Ctrl-D,Python将恢复执行(使用您所做的任何变量更新)。请注意,如果您从常规IPython IPython Shell(上面的案例2)运行代码,那么新的IPython Shell将嵌套在您从中调用它的外壳内,这很好,但是请注意。无论哪种方式,一旦解释器停在的位置ipsh,我都可以检查a2)的值,查看定义了哪些函数和对象,等等。

问题:

上面的解决方案可以使Python在代码中所需的任何位置停止,然后将您带入完整的IPython解释器。不幸的是,调用脚本后,它不允许您添加或删除断点,这非常令人沮丧。在我看来,这是阻止IPython成为Python出色的调试工具的唯一原因

您目前可以做的最好的事情是:

一种解决方法是ipsh()在要Python解释程序启动IPython Shell的不同位置(即breakpoint)放置先验先验。然后,您可以使用以下命令在不同的预定义,硬编码的“断点”之间“跳转”Ctrl-D,这将退出当前的嵌入式IPython shell,并且每当解释器单击下一个调用时再次停止ipsh()

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

(Update on May 28, 2016) Using RealGUD in Emacs

For anyone in Emacs, this thread shows how to accomplish everything described in the OP (and more) using

  1. a new important debugger in Emacs called RealGUD which can operate with any debugger (including ipdb).
  2. The Emacs package isend-mode.

The combination of these two packages is extremely powerful and allows one to recreate exactly the behavior described in the OP and do even more.

More info on the wiki article of RealGUD for ipdb.


Original answer:

After having tried many different methods for debugging Python, including everything mentioned in this thread, one of my preferred ways of debugging Python with IPython is with embedded shells.

Defining a custom embedded IPython shell:

Add the following on a script to your PYTHONPATH, so that the method ipsh() becomes available.

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)

Then, whenever I want to debug something in my code, I place ipsh() right at the location where I need to do object inspection, etc. For example, say I want to debug my_function below

Using it:

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

and then I invoke my_function(2) in one of the following ways:

  1. Either by running a Python program that invokes this function from a Unix shell
  2. Or by invoking it directly from IPython

Regardless of how I invoke it, the interpreter stops at the line that says ipsh(). Once you are done, you can do Ctrl-D and Python will resume execution (with any variable updates that you made). Note that, if you run the code from a regular IPython the IPython shell (case 2 above), the new IPython shell will be nested inside the one from which you invoked it, which is perfectly fine, but it’s good to be aware of. Eitherway, once the interpreter stops on the location of ipsh, I can inspect the value of a (which be 2), see what functions and objects are defined, etc.

The problem:

The solution above can be used to have Python stop anywhere you want in your code, and then drop you into a fully-fledged IPython interpreter. Unfortunately it does not let you add or remove breakpoints once you invoke the script, which is highly frustrating. In my opinion, this is the only thing that is preventing IPython from becoming a great debugging tool for Python.

The best you can do for now:

A workaround is to place ipsh() a priori at the different locations where you want the Python interpreter to launch an IPython shell (i.e. a breakpoint). You can then “jump” between different pre-defined, hard-coded “breakpoints” with Ctrl-D, which would exit the current embedded IPython shell and stop again whenever the interpreter hits the next call to ipsh().

If you go this route, one way to exit “debugging mode” and ignore all subsequent breakpoints, is to use ipshell.dummy_mode = True which will make Python ignore any subsequent instantiations of the ipshell object that we created above.


回答 3

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

顺便说一句,ipdb在幕后使用IPython,您实际上可以使用IPython功能,例如TAB完成和魔术命令(以开头的命令%)。如果你是IPDB OK,你可以从IPython中使用命令,如启动%run%debug。从某种意义上讲,ipdb会话实际上比普通的IPython会话要好,因为您可以在堆栈跟踪中上下移动。ipdb中“对象检查”缺少什么?

另外,与Emacs> = 24.3捆绑在一起的python.el具有不错的ipdb支持。

You can start IPython session from pudb and go back to the debugging session as you like.

BTW, ipdb is using IPython behind the scenes and you can actually use IPython functionality such as TAB completion and magic commands (the one starts with %). If you are OK with ipdb you can start it from IPython using commands such as %run and %debug. ipdb session is actually better than plain IPython one in the sense you can go up and down in the stack trace etc. What is missing in ipdb for “object inspection”?

Also, python.el bundled with Emacs >= 24.3 has nice ipdb support.


回答 4

似乎@gaborous的答案中的方法已被弃用

新方法似乎是:

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

def buggy_method():
    debug()

Looks like the approach in @gaborous’s answer is deprecated.

The new approach seems to be:

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

def buggy_method():
    debug()

回答 5

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

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

但是!help(numpy.transpose)将为您提供有关numpy.transpose的预期帮助页面。同样,对于变量名,假设您有一个变量l,在pdb中键入“ l”会列出代码,但是!l会打印出l的值。

Prefixing an “!” symbol to commands you type in pdb seems to have the same effect as doing something in an IPython shell. This works for accessing help for a certain function, or even variable names. Maybe this will help you to some extent. For example,

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

But !help(numpy.transpose) will give you the expected help page on numpy.transpose. Similarly for variable names, say you have a variable l, typing “l” in pdb lists the code, but !l prints the value of l.


回答 6

您是否尝试过此技巧

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

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

那你就可以用

debug_here()

每当你想设置一个断点

Did you try this tip?

Or better still, use ipython, and call:

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

then you can just use

debug_here()

whenever you want to set a breakpoint


回答 7

您可以IPython 从内部 开始ipdb

引入ipdb调试器1

import idpb; ipdb.set_trace()

输入IPython的从内部ipdb>控制台2

from IPython import embed; embed()

ipdb>从以下位置返回到控制台IPython

exit

如果您有幸使用Emacs,可以使事情变得更加便捷!

这需要使用M-x shell。使用yasnippetbm,定义以下代码段。这将用行替换ipdb编辑器中的文本set-trace。插入代码段后,该行将突出显示,以便易于观察和导航。使用M-x bm-next导航。

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

1全部一行,易于删除。由于imports仅发生一次,因此此表单确保ipdb将在您需要时将其导入,而不会产生额外的开销。

2,您可以通过导入自己节省一些打字IPython 的内.pdbrc文件

try:
    from IPython import embed
except:
    pass

这使您可以简单地embed()从内部进行调用ipdb(当然,仅在安装IPython时)。

You can start IPython from within ipdb!

Induce the ipdb debugger1:

import idpb; ipdb.set_trace()

Enter IPython from within in the ipdb> console2:

from IPython import embed; embed()

Return to the ipdb> console from within IPython:

exit

If you’re lucky enough to be using Emacs, things can be made even more convenient!

This requires using M-x shell. Using yasnippet and bm, define the following snippet. This will replace the text ipdb in the editor with the set-trace line. After inserting the snippet, the line will be highlighted so that it is easily noticeable and navigable. Use M-x bm-next to navigate.

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

1 All on one line for easy deletion. Since imports only happen once, this form ensures ipdb will be imported when you need it with no extra overhead.

2 You can save yourself some typing by importing IPython within your .pdbrc file:

try:
    from IPython import embed
except:
    pass

This allows you to simply call embed() from within ipdb (of course, only when IPython is installed).


回答 8

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

One option is to use an IDE like Spyder which should allow you to interact with your code while debugging (using an IPython console, in fact). In fact, Spyder is very MATLAB-like, which I presume was intentional. That includes variable inspectors, variable editing, built-in access to documentation, etc.


回答 9

该问题的正确,简单,酷爽的准确答案是将%run宏与-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

the right, easy, cool, exact answer for the question is to use %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

回答 10

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

If you type exit() in embed() console the code continue and go to the next embed() line.


回答 11

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

但是,除非您设置了另一个断点,否则似乎单步执行(还好吗?)效果不佳(即在一行中停下来,然后步入另一功能)。

仍然来自MATLAB,这似乎是我找到的最佳解决方案。

The Pyzo IDE has similar capabilities as the OP asked for. You don’t have to start in debug mode. Similarly to MATLAB, the commands are executed in the shell. When you set up a break-point in some source code line, the IDE stops the execution there and you can debug and issue regular IPython commands as well.

It does seem however that step-into doesn’t (yet?) work well (i.e. stopping in one line and then stepping into another function) unless you set up another break-point.

Still, coming from MATLAB, this seems the best solution I’ve found.


回答 12

在python 3.2中,您具有interact命令,该命令可让您访问完整的python / ipython命令空间。

From python 3.2, you have the interact command, which gives you access to the full python/ipython command space.


回答 13

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

与python-mode.el,Mx ipython RET等一起检查

Running from inside Emacs’ IPython-shell and breakpoint set via pdb.set_trace() should work.

Checked with python-mode.el, M-x ipython RET etc.


回答 14

开发新代码

在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.embed()用于在调试器中需要时具有完整的IPython功能

关于Python的想法

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

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

另请参见是否可以通过调试在IPython中运行命令?

Developing New Code

Debugging inside IPython

  1. Use Jupyter/IPython cell execution to speed up experiment iterations
  2. Use %%debug for step through

Cell Example:

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

Debugging Existing Code

IPython inside debugging

  1. Debugging a broken unit test: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. Debugging outside of test case: breakpoint(), python -m ipdb, etc.
  3. IPython.embed() for full IPython functionality where needed while in the debugger

Thoughts on Python

I agree with the OP that many things MATLAB does nicely Python still does not have and really should since just about everything in the language favors development speed over production speed. Maybe someday I will contribute more than trivial bug fixes to CPython.

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

See also Is it possible to run commands in IPython with debugging?


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。