【问题标题】:Jupyter magic to handle notebook exceptions处理笔记本异常的 Jupyter 魔法
【发布时间】:2017-02-27 20:42:42
【问题描述】:

我的 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 中,对吗?但这太乏味了。

提前感谢您的任何建议。

【问题讨论】:

    标签: python jupyter jupyter-notebook


    【解决方案1】:

    这种神奇的命令是不存在的,但你可以自己写。

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

    【讨论】:

    • try...finally... 块可能更适合如果 OP 想要接收电子邮件无论哪个异常被提出。在try...except... 中,它应该处理异常,现在不是,另一种方法是重新引发它。 IMO,finally 更健壮。
    • @x0s 带有try...finally... 块,邮件将为每个执行的单元格发送;这不是OP想要的。但重新提出它是个好主意。我编辑了。
    • 当我使用exec 时访问在其他单元格中定义的变量时遇到问题。但是当我使用get_ipython().run_cell(cell) 时,我无法捕捉到异常。你是怎么解决这个问题的?
    • 是否有可能使用两个魔术命令 %% handle 然后 %run .../someNoteBook - 如果笔记本不存在则需要处理异常
    【解决方案2】:

    @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注意事项:将这个sn-p变成一个小python模块,可以导入笔记本并通过line magic激活。

    不过要小心。该文档包含此 set_custom_exc 方法的警告:“警告:通过将您自己的异常处理程序放入 IPython 的主执行循环中,您很有可能会发生严重的崩溃。只有在您真的知道自己的情况下才应该使用此工具正在做。”

    【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

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

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

      在此示例中,我希望打印错误(但可以是任何内容,例如开头帖子中的电子邮件),但在下一个单元格中,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】:

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

        不过,runtools extension 可以解决问题。

        【讨论】:

          猜你喜欢
          • 2017-10-25
          • 1970-01-01
          • 2021-06-14
          • 2020-02-21
          • 2020-08-14
          • 2019-11-08
          • 2019-03-23
          • 1970-01-01
          • 2020-06-11
          相关资源
          最近更新 更多