处理笔记本异常的 Jupyter 魔法

Jupyter magic to handle notebook exceptions

我在我的 Jupyter Notebook 中进行了一些长期的 运行 实验。因为我不知道他们什么时候完成,所以我在笔记本的最后一个单元格添加了一个电子邮件功能,所以当笔记本完成时我会自动收到一封电子邮件。

但是当其中一个单元格出现随机异常时,整个笔记本停止执行,我再也没有收到任何电子邮件。 所以我想知道是否有一些神奇的函数可以在异常/内核停止的情况下执行函数。

喜欢

def handle_exception(stacktrace):
    send_mail_to_myself(stacktrace)


%%in_case_of_notebook_exception handle_exception # <--- this is what I'm looking for

另一种选择是将每个单元封装在 try-catch 中,对吗?但这太乏味了。

提前感谢您的任何建议。

我不认为有一种开箱即用的方法可以做到这一点,而不是在您的单元格中使用 try..except 语句。 AFAIK a 4 years old issue 提到了这一点,但仍处于开放状态。

但是,runtools extension 可能会起作用。

这样的魔法命令是不存在的,但是你可以自己写。

from IPython.core.magic import register_cell_magic

@register_cell_magic('handle')
def handle(line, cell):
    try:
        exec(cell)
    except Exception as e:
        send_mail_to_myself(e)
        raise # if you want the full trace-back in the notebook

无法为整个笔记本自动加载魔术命令,您必须在需要此功能的每个单元格中添加它。

%%handle

some_code()
raise ValueError('this exception will be caught by the magic command')

@show0k 对我的问题给出了正确的答案(关于魔术方法)。非常感谢! :)

这个答案激发了我深入挖掘的灵感,我发现了一个 IPython 方法,可以让您为整个笔记本定义一个 自定义异常处理程序

我是这样工作的:

from IPython.core.ultratb import AutoFormattedTB

# initialize the formatter for making the tracebacks into strings
itb = AutoFormattedTB(mode = 'Plain', tb_offset = 1)

# this function will be called on exceptions in any cell
def custom_exc(shell, etype, evalue, tb, tb_offset=None):

    # still show the error within the notebook, don't just swallow it
    shell.showtraceback((etype, evalue, tb), tb_offset=tb_offset)

    # grab the traceback and make it into a list of strings
    stb = itb.structured_traceback(etype, evalue, tb)
    sstb = itb.stb2text(stb)

    print (sstb) # <--- this is the variable with the traceback string
    print ("sending mail")
    send_mail_to_myself(sstb)

# this registers a custom exception handler for the whole current notebook
get_ipython().set_custom_exc((Exception,), custom_exc)

所以这可以放在任何笔记本顶部的单个单元格中,因此它会在出现问题时进行邮寄。

自我注意事项/TODO:将此片段制作成一个小 python 模块,可以将其导入笔记本并通过 line magic 激活。

不过要小心。该文档包含针对此 set_custom_exc 方法的警告:"WARNING: by putting in your own exception handler into IPython’s main execution loop, you run a very good chance of nasty crashes. This facility should only be used if you really know what you are doing."

为什么 exec 并不总是解决方案

几年后,我在尝试使用 Jupyter magics 处理错误时遇到了类似的问题。但是,我需要变量来保留在实际的 Jupyter notebook 中。

%%try_except print
a = 12
raise ValueError('test')

在此示例中,我希望打印错误(但可以是任何内容,例如开头 post 中的电子邮件),而且 a == 12 在下一个单元格中为真.因此,当您在不同的文件中定义魔术时,建议的方法 exec 不起作用。我找到的解决方案是使用 IPython 功能。

如何解决

from IPython.core.magic import line_magic, cell_magic, line_cell_magic, Magics, magics_class


@magics_class
class CustomMagics(Magics):
    @cell_magic
    def try_except(self, line, cell):
        """ This magic wraps a cell in try_except functionality """  
        try:
            self.shell.ex(cell)  # This executes the cell in the current namespace
        except Exception as e:
            if ip.ev(f'callable({how})'):  # check we have a callable handler
                self.shell.user_ns['error'] = e  # add error to namespace
                ip.ev(f'{how}(error)')  # call the handler with the error
            else:
                raise e


# Register
from IPython import get_ipython
ip = get_ipython()
ip.register_magics(CustomMagics)

从笔记本 5.1 开始,您可以使用新标签:raises-exception 这将表明特定单元格中的异常是预期的,jupyter 不会停止执行。

(要设置标签,您必须从主菜单中选择:查看 -> 单元格工具栏 -> 标签)