Jupyter魔法函数处理笔记本异常

35

我在Jupyter Notebooks中运行了一些长时间的实验。由于我不知道它们何时会结束,因此我在笔记本的最后一个单元格中添加了一个电子邮件功能,这样当笔记本完成时,我就可以自动收到电子邮件。

但是,如果某个单元格中出现随机异常,整个笔记本将停止执行,我将无法收到任何电子邮件。因此我想知道是否有一些神奇的函数可以在出现异常/内核停止的情况下执行一个函数。

例如:

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中,对吗?但是那太繁琐了。

提前感谢任何建议。

5个回答

28

@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)

这样,可以将此内容放入笔记本顶部的单元格中,如果出现问题,它将执行邮件发送。

自己注意/待办事项:将此片段制作成一个小型Python模块,可导入笔记本并通过行魔法激活。

但要小心。文档中对于此set_custom_exc方法有警告:“警告:通过将自己的异常处理程序放入IPython的主执行循环中,您很有可能遇到严重崩溃。只有在确实知道自己在做什么时,才应使用此功能。”


2
我重复使用了您美丽的答案来添加声音 > https://stackoverflow.com/questions/61176900/jupyter-colab-play-sound-with-any-error-in-any-cell-play-sound-after-compl/61176901 - Rub

27

没有这样的神奇命令,但你可以自己编写一个。

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')

如果OP想要收到引发的任何异常的电子邮件,那么try...finally...块可能更合适。在try...except...中,需要处理异常,但现在没有处理,另一种方法是重新引发它。在我看来,finally更加健壮。 - x0s
1
通过使用try...finally...块,@x0s的代码会在每个执行的单元格后发送邮件;这不是OP想要的。但重新引发异常是一个好主意。我已经进行了编辑。 - show0k
当我使用exec时,我遇到了访问在其他单元格中定义的变量的问题。但是当我使用get_ipython().run_cell(cell)时,我无法捕获异常。你是如何解决这个问题的? - Roelant
有没有可能用两个神奇的命令 %% handle 和 %run .../someNoteBook 来实现这个?如果笔记本不存在,需要处理异常。 - WeisserHund

15
自从Notebook 5.1版本起,你可以使用一个新的标签:raises-exception。这将表示特定单元格中的异常是预期的,并且jupyter不会停止执行。
(为了设置标签,您必须从主菜单中选择:View-> Cell Toolbar-> Tags)

在JupyterLab 4中,设置标签是通过按下通常显示在最右侧边栏上的齿轮后弹出的“属性检查器”界面来访问的。在Jupyter Notebook 7+中,您可以获得类似JupyterLab的界面,在主菜单中选择“视图”->“右侧边栏”->“显示笔记本工具”,从而可以编辑元标签。 - undefined

3

为什么exec不总是解决方案

几年过去了,我遇到了类似的问题,在尝试处理Jupyter魔法函数错误时。但是,我需要变量在实际的Jupyter笔记本中持久存在。

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

在这个例子中,我希望能够打印错误(但也可以是任何内容,比如开头的邮件),同时在下一个单元格中 a == 12 也要为 true。因此,当您在不同文件中定义魔法时,建议不要使用exec 方法。我找到的解决方法是使用 IPython 的功能。
你可以通过使用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)

CustomMagics 的好例子!不幸的是,这个魔法会改变单元格的行为,导致单元格中最后一个表达式的结果没有被打印出来。你知道如何在 CustomMagics 中修复这个问题吗?我已经检查过 self.shell.ex(cell) 并没有返回这个值 :( - pabouk - Ukraine stay strong

2
我认为没有一种开箱即用的方法来实现这个功能,除非在单元格中使用try..except语句。据我所知一个4年前的问题提到了这个问题,但仍然是未解决状态。
然而,runtools扩展程序可能能够解决这个问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接