13

Normally unhandled exceptions go to the stdout (or stderr?), I am building an app where I want to pass this info to the GUI before shutting down and display it to the user and, at the same time I want to write it to a log file. So, I need an str with the full text of the exception.

How can I do this?

George
  • 15,241
  • 22
  • 66
  • 83

5 Answers5

23

Use sys.excepthook to replace the base exception handler. You can do something like:

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

This catches all unhandled exceptions, so you don't need a try...except block at the top level of your code.

Neil
  • 2,378
  • 1
  • 20
  • 28
  • 2
    It caches all unhandled exceptions in the main thread, but if you use the threading module, threading.Thread has its own try/except handler which circumvents sys.excepthook. See http://bugs.python.org/issue1230540. – metamatt Jul 05 '13 at 07:06
  • this is very useful now i can handle all exceptions and send those exceptions to server – timelessnesses Apr 11 '22 at 19:56
11

You already got excellent answers, I just wanted to add one more tip that's served me well over the years in a variety of language for the specific problem "how to cleanly diagnose, log, etc, out of memory errors?". Problem is, if your code gets control before enough objects have been destroyed and their memory recycled, memory might be too tight to do propert logging, gui work, etc, etc -- how do we ensure this doesn't happen?

Answer: build an emergency stash so you know you can spend it in such emergencies:

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

def handle_exception(e):
  global rainydayfund
  del rainydayfund
  ... etc, etc ...
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
1
import sys, logging

logging.basicConfig(filename='/path/to/log/file', filemode='w')    
...

try:
   your_code_here()
except:
   logging.exception("My code failed") # logs exception to file
   # you define display_exception_in_ui as "def display_exception_in_ui(exc, tb):"
   display_exception_in_ui(*sys.exc_info()[1:]) # passes exception instance, traceback
Vinay Sajip
  • 95,872
  • 14
  • 179
  • 191
1

I tried using Neil's answer, but it doesn't work with a Tkinter GUI. For that, I had to override 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()
Community
  • 1
  • 1
Don Kirkby
  • 53,582
  • 27
  • 205
  • 286
0
try:
    # blah blah The Main Loop, function, whatever...
except e:
    do_something_with(str(e))
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662