Introduction
The handling of exceptions seems to come up regularly on the wxPython list. During a recent thread about this I mentioned what I am doing with exceptions and Tal Einat suggested that the code might be helpful to others.
Maybe others can add their way of doing things here, and/or correct/enhance whatever I am doing.
What Objects are Involved
* wx.App - force redirection of exception
* wx.App.OnInit - setup the custom exception handler to write exceptions to a log file
* dialogerror.py - display the error log files and allow them to be sent by eMail to support.
Process Overview
A custom exception handler is setup to catch un-handled errors, stdout and stderr are redirected to log files. When an exception is caught a dialog is shown with a message informing the user that an error occurred (it also provides system information such as what platform and version the user is using), it shows the log files and allows the user to send the log files via SMTP or MAPI or just to clear the log files. My application uses a SQL db and I therefor included a third log file which can show the SQL commands.
Everything is also I18N enabled, so the dialog etc can be translated.
The zip file includes English and German translation and a little test errorDiagTestApp.py, just run it and click on the appropriate button to see it in action.
Special Concerns
simplemapi.py (created by Ian Cook) is needed for the sending of the eMail using MAPI on Windows (included in the ZIP).
Code snippets
Setting up folders, redirecting stdout and stderr and redefining the sys.excepthook
The custom exception handler, which writes a time stamp to the file, then the traceback and then it displays the dialog (in ReportException).
1 def MyExceptionHandler(self, type, value, trace_back):
2 """Catch exceptions, log them to file and show error dialog
3 """
4 timestamp = myTime.asctime(myTime.localtime(myTime.time()))
5 self.stderrlog.write('**** %s ****\n' % timestamp)
6 traceback.print_exception(type, value, trace_back, file=self.stderrlog)
7 self.stderrlog.write('\n')
8 # to ensure that errorDialog is shown immediately
9 self.stderrlog.flush()
10
11 self.ReportException(msg=2)
I have two slightly different messages defined, '1' an error occurred during a previous session and '2' an error was just caught.
Some settings like eMail address and SMTP server name are saved to a wx.ConfigFile.
The .flush and .seek on the log files are needed to ensure that the code detects that there is something in the log files (as only then will the dialog be shown).
1 def ReportException(self, msg = 1):
2 self.SetErrDiagMsg()
3 if msg == 1:
4 self.msg = self.msg1
5 else:
6 self.msg = self.msg2
7
8 self.msg += '\n'
9 self.msg += ('Language = %s' % self.appConfig.Read(key='Language'))
10
11 self.eMail = self.appConfig.Read(key='EMail')
12 self.smtpServer = self.appConfig.Read(key='SMTPServer')
13 myErr = False
14 self.stdoutlog.seek(0)
15 self.stderrlog.seek(0)
16 self.sqllog.seek(0)
17
18 if len(self.stdoutlog.read()) >0:
19 myErr = True
20 if len(self.stderrlog.read()) >0:
21 myErr = True
22 if len(self.sqllog.read()) >0:
23 myErr = True
24
25 if myErr:
26 self.stdoutlog.seek(0)
27 self.stderrlog.seek(0)
28 self.sqllog.seek(0)
29
30 dlg = dialogerror.DialogError(self)
31 dlg.CenterOnScreen()
32 try:
33 dlg.ShowModal()
34 finally:
35 dlg.Destroy()
36
37 self.stdoutlog.seek(0, 2)
38 self.stderrlog.seek(0, 2)
39 self.sqllog.seek(0, 2)
Comments
The attached ZIP file includes all the code and a test script. errorDialog.zip
This is also a good place for others to make comments and or suggestions.