Python:如何以其他方式处理未处理的异常?

13 投票
5 回答
16281 浏览
提问于 2025-04-15 13:24

通常情况下,未处理的错误信息会输出到标准输出(stdout)或者标准错误(stderr)。我正在开发一个应用程序,想在程序关闭之前把这些错误信息传递给图形界面(GUI),并显示给用户。同时,我还想把这些信息写入一个日志文件。所以,我需要一个字符串,里面包含完整的错误信息。

我该怎么做呢?

5 个回答

1

我试过使用Neil的回答,但是在Tkinter图形界面下不管用。为了实现这个功能,我不得不重写report_callback_exception()

import Tkinter as tk
import tkMessageBox
import traceback

class MyApp(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        parent.report_callback_exception = self.report_callback_exception
        self.parent = parent
        self.button_frame = tk.Frame(self)
        self.button_frame.pack(side='top')
        self.button_run = tk.Button(
            self.button_frame, text="Run", command=self.run
        )
        self.button_run.grid(row=0, column=1, sticky='W')

    def run(self):
        tkMessageBox.showinfo('Info', 'The process is running.')
        raise RuntimeError('Tripped.')

    def report_callback_exception(self, exc_type, exc_value, exc_traceback):
        message = ''.join(traceback.format_exception(exc_type,
                                                     exc_value,
                                                     exc_traceback))
        tkMessageBox.showerror('Error', message)

def main():
    root = tk.Tk()  # parent widget

    MyApp(root).pack(fill='both', expand=True)

    root.mainloop()  # enter Tk event loop

if __name__ == '__main__':
    main()
11

你已经得到了很好的答案,我只是想再补充一个我多年来在不同编程语言中都很受用的小技巧,专门针对“如何干净利落地诊断、记录等内存溢出错误?”这个问题。问题在于,如果你的代码在足够多的对象被销毁和它们的内存被回收之前就开始运行,内存可能会变得非常紧张,这样就无法进行正常的记录、图形界面操作等等。那么,我们该如何确保这种情况不会发生呢?

答案是:建立一个应急存储,这样你就知道在紧急情况下可以使用它:

rainydayfund = [[] for x in xrange(16*1024)]  # or however much you need

def handle_exception(e):
  global rainydayfund
  del rainydayfund
  ... etc, etc ...
23

使用 sys.excepthook 来替换默认的异常处理器。你可以这样做:

import sys
from PyQt4 import QtGui

import os.path
import traceback

def handle_exception(exc_type, exc_value, exc_traceback):
  """ handle all exceptions """

  ## KeyboardInterrupt is a special case.
  ## We don't raise the error dialog when it occurs.
  if issubclass(exc_type, KeyboardInterrupt):
    if QtGui.qApp:
      QtGui.qApp.quit()
    return

  filename, line, dummy, dummy = traceback.extract_tb( exc_traceback ).pop()
  filename = os.path.basename( filename )
  error    = "%s: %s" % ( exc_type.__name__, exc_value )

  QtGui.QMessageBox.critical(None,"Error",
    "<html>A critical error has occured.<br/> "
  + "<b>%s</b><br/><br/>" % error
  + "It occurred at <b>line %d</b> of file <b>%s</b>.<br/>" % (line, filename)
  + "</html>")

  print "Closed due to an error. This is the full error report:"
  print
  print "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
  sys.exit(1)



# install handler for exceptions
sys.excepthook = handle_exception

这样做可以捕捉到所有未处理的异常,所以你在代码的最上层就不需要写 try...except 这种结构了。

撰写回答