Welcome, guest | Sign In | My Account | Store | Cart

A Python 2.4 (or later) decorator can be used to watch for unhandled exceptions, even in hybrid environments like wxPython. You place the decorator around methods that will be invoked by the wxWidgets code. The only big trick is a dance that may be necessary to provide access to values only available after the wxPthon app is started.

Python, 76 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import wx, os, sys
errorframe = None

def watcherrors(function):
     '''function decorator to display Exception information.'''
     def substitute(*args, **kwargs):
         try:
             return function(*args, **kwargs)
         except Exception:
             error_type, error, traceback = sys.exc_info()
             mb = wx.MessageDialog(errorframe,
                         '%s\n\nClick OK to see traceback' % error,
                         'Error in Run',
                         wx.OK | wx.CANCEL | wx.ICON_ERROR)
             if wx.ID_OK == mb.ShowModal():
                 mb.Destroy()
                 from traceback import extract_tb
                 trace = ['%s line %s in %s:\n\t%s' % (
                              os.path.split(filename)[1], line, fun, text)
                          for filename, line, fun, text in
                                  extract_tb(traceback)]
                 mb = wx.MessageDialog(errorframe,
                         '\n'.join(['%s\n' % error] + trace),
                         'Run Error w/ Traceback',
                         wx.OK | wx.ICON_ERROR)
                 result = mb.ShowModal()
             mb.Destroy()
             raise  # Optional-- you may want to squech some errors here

         try:
             substitute.__doc__ = function.__doc__
         except AttributeError:
             pass

         return substitute

# You can use it as follows to wrap functions and methods:
'''
class ....

     @watcherrors
     def something(somearg)
         if somearg is not None:
                 raise ValueError(somearg)
     ...
'''

# Alternative: (watcherrors is defined almost as above)

def augmentedwatch(holder, framefieldname):
    def watcherrors(function:
        '...' # -- same as above
        def substitute(*args, **kwargs):
            try:
                return function(*args, **kwargs)
            except Exception:
                error_type, error, traceback = sys.exc_info()
                ### new code:
                errorframe = getattr(holder, framefieldname, None) 
                '...' # -- same as above
        return substitute
    return watcherrors

# You use this as follows to wrap functions and methods, where 
# holder is an expression at "first import" time that will, at
# the time of one of the uncaught exceptions, have an attribute 
# named 'frame':
''' 
class ....

     @augmentedwatch(holder, 'frame')
     def something(somearg)
         if somearg is not None:
                 raise ValueError(somearg)
     ...
'''

Wrapping the code this way allows you to say, watcher-by-watcher, where to get the parent for the message box. You will know which of your methods will need to be wrapped; only those tied to wx events. "watcherrors" gets any information it needs from globals in the module defining watcherrors. Often, that is not such a great idea.

In case the "watcherrors" technique doesn't work for your code structure, you may need another layer of function-producing function (augmentedwatch) which knows where to get the data from. The 'holder' parameter needs to exist when the methods (or functions) are being defined. One possible holder is a class name already defined above in the same source. Another possible holder is a module name defined in an import statement, and a final possibility is an expression like "__import('__main__')".

Again, for augmentedwatch, the 'holder' expression is evaluated when defining the function or method. However, holder need not have the 'framefieldname' field appropriately defined until the moment the exception is caught.

Created by Scott David Daniels on Thu, 10 Mar 2005 (PSF)
Python recipes (4591)
Scott David Daniels's recipes (10)

Required Modules

  • (none specified)

Other Information and Tasks