如何通过 `jupyter-console --existing` 调用基于 `InteractiveShellEmbed` 的自定义异常钩子
主要问题:我该如何在Jupyter笔记本的IPython内核中,运行一个完整功能的嵌入式IPython命令行(simple_prompt=False
),并且在发生异常的框架命名空间中进行调试?
解释:
我有一个使用场景(几乎解决了,但还有一个主要缺陷):
在Jupyter笔记本中(使用Python内核),我运行一个嵌套的函数调用failing_function()
。在调用的深层次中发生了异常。我想在命令行中使用我的自定义异常处理钩子(ipydex.ips_excepthook
)来调试这个情况;它会在异常发生的框架中打开一个嵌入式IPython命令行,并提供向上和向下移动调用栈的可能性,我觉得这比传统的IPython调试器更有用。
我可以通过以下步骤实现这一点:在一个普通的Python脚本中:
import sys
from jupyter_console.app import ZMQTerminalIPythonApp
# mimic jupyter-console --existing: attach shell to running kernel
sys.argv = [sys.argv[0], "--existing"]
app = ZMQTerminalIPythonApp.instance()
app.initialize(None)
super(ZMQTerminalIPythonApp, app).start()
code = """
import ipydex
import traceback
try:
failing_function()
except Exception as ex:
value, tb = traceback._parse_value_tb(ex, traceback._sentinel, traceback._sentinel)
ipydex.ips_excepthook(type(ex), ex, tb)
"""
# run this code in the context of the running kernel
app.shell.run_cell(code)
问题:
这个方法可以实现,但有一个缺陷:它只提供了嵌入式IPython命令行的simple_prompt
模式。背景是:我的自定义异常处理钩子内部创建了一个from IPython.terminal.embed.InteractiveShellEmbed
的实例,并调用它的主循环(这是预期的)。这个主循环有两种不同的提示模式:默认模式和简单模式。默认模式有很多我想要的功能(比如彩色输出、TAB补全等),而简单模式则没有。这种模式是由这一行代码决定的:IPython/terminal/interactiveshell.py#L134。
我尝试强制将类变量InteractiveShellEmbed.simple_prompt
设置为False
,但结果却出现了RuntimeError: This event loop is already running
(这就太简单了)。
我看到有三种可能的方法可以实现我想要的效果:
- a) 让
InteractiveShellEmbed
在正在运行的ZMQTerminalIPythonApp
中以默认模式(即非简单模式)运行。 - b) 直接在发生异常的框架命名空间中运行
ZMQTerminalIPythonApp.shell.mainloop()
(并提供向上和向下移动调用栈的可能性)。 - c) 暂时用我的自定义
ipydex.ips_excepthook
覆盖sys.excepthook
,并直接执行failing_function()
而不使用try ... except
。
对于a):整个自定义异常处理钩子的故事只是我的动机。问题归结为让app.shell.run_cell('IPython.embed(colors="neutral")')
在默认模式下运行(而不是简单模式)。对于b):我不知道这是否可行,因为InteractiveShellEmbed.mainloop
接受关键字参数local_ns
和module
(全局命名空间),而ZMQTerminalIPythonApp.shell.mainloop
则不接受。对于c):这在普通脚本中完美可行,但在“注入”代码到正在运行的Jupyter内核时却不行,因为覆盖sys.excepthook
似乎没有效果。
衍生问题:
- 哪种方法(a、b、c)最有希望?
- 下一步该怎么做比较好?
- 我还有哪些其他可能性?
0 个回答
暂无回答