##// END OF EJS Templates
Cleaner error message in crash handler.
Fernando Perez -
Show More
@@ -1,229 +1,229 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
3
3
4
4
5 Authors
5 Authors
6 -------
6 -------
7 - Fernando Perez <Fernando.Perez@berkeley.edu>
7 - Fernando Perez <Fernando.Perez@berkeley.edu>
8 """
8 """
9
9
10 #*****************************************************************************
10 #*****************************************************************************
11 # Copyright (C) 2008-2009 The IPython Development Team
11 # Copyright (C) 2008-2009 The IPython Development Team
12 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
12 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #*****************************************************************************
16 #*****************************************************************************
17
17
18 #****************************************************************************
18 #****************************************************************************
19 # Required modules
19 # Required modules
20
20
21 # From the standard library
21 # From the standard library
22 import os
22 import os
23 import sys
23 import sys
24 from pprint import pformat
24 from pprint import pformat
25
25
26 # Our own
26 # Our own
27 from IPython.core import release
27 from IPython.core import release
28 from IPython.core import ultratb
28 from IPython.core import ultratb
29 from IPython.external.Itpl import itpl
29 from IPython.external.Itpl import itpl
30
30
31 from IPython.utils.genutils import *
31 from IPython.utils.genutils import *
32
32
33 #****************************************************************************
33 #****************************************************************************
34 class CrashHandler:
34 class CrashHandler:
35 """Customizable crash handlers for IPython-based systems.
35 """Customizable crash handlers for IPython-based systems.
36
36
37 Instances of this class provide a __call__ method which can be used as a
37 Instances of this class provide a __call__ method which can be used as a
38 sys.excepthook, i.e., the __call__ signature is:
38 sys.excepthook, i.e., the __call__ signature is:
39
39
40 def __call__(self,etype, evalue, etb)
40 def __call__(self,etype, evalue, etb)
41
41
42 """
42 """
43
43
44 def __init__(self,IP,app_name,contact_name,contact_email,
44 def __init__(self,IP,app_name,contact_name,contact_email,
45 bug_tracker,crash_report_fname,
45 bug_tracker,crash_report_fname,
46 show_crash_traceback=True):
46 show_crash_traceback=True):
47 """New crash handler.
47 """New crash handler.
48
48
49 Inputs:
49 Inputs:
50
50
51 - IP: a running IPython instance, which will be queried at crash time
51 - IP: a running IPython instance, which will be queried at crash time
52 for internal information.
52 for internal information.
53
53
54 - app_name: a string containing the name of your application.
54 - app_name: a string containing the name of your application.
55
55
56 - contact_name: a string with the name of the person to contact.
56 - contact_name: a string with the name of the person to contact.
57
57
58 - contact_email: a string with the email address of the contact.
58 - contact_email: a string with the email address of the contact.
59
59
60 - bug_tracker: a string with the URL for your project's bug tracker.
60 - bug_tracker: a string with the URL for your project's bug tracker.
61
61
62 - crash_report_fname: a string with the filename for the crash report
62 - crash_report_fname: a string with the filename for the crash report
63 to be saved in. These reports are left in the ipython user directory
63 to be saved in. These reports are left in the ipython user directory
64 as determined by the running IPython instance.
64 as determined by the running IPython instance.
65
65
66 Optional inputs:
66 Optional inputs:
67
67
68 - show_crash_traceback(True): if false, don't print the crash
68 - show_crash_traceback(True): if false, don't print the crash
69 traceback on stderr, only generate the on-disk report
69 traceback on stderr, only generate the on-disk report
70
70
71
71
72 Non-argument instance attributes:
72 Non-argument instance attributes:
73
73
74 These instances contain some non-argument attributes which allow for
74 These instances contain some non-argument attributes which allow for
75 further customization of the crash handler's behavior. Please see the
75 further customization of the crash handler's behavior. Please see the
76 source for further details.
76 source for further details.
77 """
77 """
78
78
79 # apply args into instance
79 # apply args into instance
80 self.IP = IP # IPython instance
80 self.IP = IP # IPython instance
81 self.app_name = app_name
81 self.app_name = app_name
82 self.contact_name = contact_name
82 self.contact_name = contact_name
83 self.contact_email = contact_email
83 self.contact_email = contact_email
84 self.bug_tracker = bug_tracker
84 self.bug_tracker = bug_tracker
85 self.crash_report_fname = crash_report_fname
85 self.crash_report_fname = crash_report_fname
86 self.show_crash_traceback = show_crash_traceback
86 self.show_crash_traceback = show_crash_traceback
87
87
88 # Hardcoded defaults, which can be overridden either by subclasses or
88 # Hardcoded defaults, which can be overridden either by subclasses or
89 # at runtime for the instance.
89 # at runtime for the instance.
90
90
91 # Template for the user message. Subclasses which completely override
91 # Template for the user message. Subclasses which completely override
92 # this, or user apps, can modify it to suit their tastes. It gets
92 # this, or user apps, can modify it to suit their tastes. It gets
93 # expanded using itpl, so calls of the kind $self.foo are valid.
93 # expanded using itpl, so calls of the kind $self.foo are valid.
94 self.user_message_template = """
94 self.user_message_template = """
95 Oops, $self.app_name crashed. We do our best to make it stable, but...
95 Oops, $self.app_name crashed. We do our best to make it stable, but...
96
96
97 A crash report was automatically generated with the following information:
97 A crash report was automatically generated with the following information:
98 - A verbatim copy of the crash traceback.
98 - A verbatim copy of the crash traceback.
99 - A copy of your input history during this session.
99 - A copy of your input history during this session.
100 - Data on your current $self.app_name configuration.
100 - Data on your current $self.app_name configuration.
101
101
102 It was left in the file named:
102 It was left in the file named:
103 \t'$self.crash_report_fname'
103 \t'$self.crash_report_fname'
104 If you can email this file to the developers, the information in it will help
104 If you can email this file to the developers, the information in it will help
105 them in understanding and correcting the problem.
105 them in understanding and correcting the problem.
106
106
107 You can mail it to: $self.contact_name at $self.contact_email
107 You can mail it to: $self.contact_name at $self.contact_email
108 with the subject '$self.app_name Crash Report'.
108 with the subject '$self.app_name Crash Report'.
109
109
110 If you want to do it now, the following command will work (under Unix):
110 If you want to do it now, the following command will work (under Unix):
111 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
111 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
112
112
113 To ensure accurate tracking of this issue, please file a report about it at:
113 To ensure accurate tracking of this issue, please file a report about it at:
114 $self.bug_tracker
114 $self.bug_tracker
115 """
115 """
116
116
117 def __call__(self,etype, evalue, etb):
117 def __call__(self,etype, evalue, etb):
118 """Handle an exception, call for compatible with sys.excepthook"""
118 """Handle an exception, call for compatible with sys.excepthook"""
119
119
120 # Report tracebacks shouldn't use color in general (safer for users)
120 # Report tracebacks shouldn't use color in general (safer for users)
121 color_scheme = 'NoColor'
121 color_scheme = 'NoColor'
122
122
123 # Use this ONLY for developer debugging (keep commented out for release)
123 # Use this ONLY for developer debugging (keep commented out for release)
124 #color_scheme = 'Linux' # dbg
124 #color_scheme = 'Linux' # dbg
125
125
126 try:
126 try:
127 rptdir = self.IP.ipython_dir
127 rptdir = self.IP.ipython_dir
128 except:
128 except:
129 rptdir = os.getcwd()
129 rptdir = os.getcwd()
130 if not os.path.isdir(rptdir):
130 if not os.path.isdir(rptdir):
131 rptdir = os.getcwd()
131 rptdir = os.getcwd()
132 report_name = os.path.join(rptdir,self.crash_report_fname)
132 report_name = os.path.join(rptdir,self.crash_report_fname)
133 # write the report filename into the instance dict so it can get
133 # write the report filename into the instance dict so it can get
134 # properly expanded out in the user message template
134 # properly expanded out in the user message template
135 self.crash_report_fname = report_name
135 self.crash_report_fname = report_name
136 TBhandler = ultratb.VerboseTB(color_scheme=color_scheme,
136 TBhandler = ultratb.VerboseTB(color_scheme=color_scheme,
137 long_header=1)
137 long_header=1)
138 traceback = TBhandler.text(etype,evalue,etb,context=31)
138 traceback = TBhandler.text(etype,evalue,etb,context=31)
139
139
140 # print traceback to screen
140 # print traceback to screen
141 if self.show_crash_traceback:
141 if self.show_crash_traceback:
142 print >> sys.stderr, traceback
142 print >> sys.stderr, traceback
143
143
144 # and generate a complete report on disk
144 # and generate a complete report on disk
145 try:
145 try:
146 report = open(report_name,'w')
146 report = open(report_name,'w')
147 except:
147 except:
148 print >> sys.stderr, 'Could not create crash report on disk.'
148 print >> sys.stderr, 'Could not create crash report on disk.'
149 return
149 return
150
150
151 # Inform user on stderr of what happened
151 # Inform user on stderr of what happened
152 msg = itpl('\n'+'*'*70+'\n'+self.user_message_template)
152 msg = itpl('\n'+'*'*70+'\n'+self.user_message_template)
153 print >> sys.stderr, msg
153 print >> sys.stderr, msg
154
154
155 # Construct report on disk
155 # Construct report on disk
156 report.write(self.make_report(traceback))
156 report.write(self.make_report(traceback))
157 report.close()
157 report.close()
158 raw_input("Press enter to exit:")
158 raw_input("Hit <Enter> to quit this message (your terminal may close):")
159
159
160 def make_report(self,traceback):
160 def make_report(self,traceback):
161 """Return a string containing a crash report."""
161 """Return a string containing a crash report."""
162
162
163 sec_sep = '\n\n'+'*'*75+'\n\n'
163 sec_sep = '\n\n'+'*'*75+'\n\n'
164
164
165 report = []
165 report = []
166 rpt_add = report.append
166 rpt_add = report.append
167
167
168 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
168 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
169 rpt_add('IPython version: %s \n\n' % release.version)
169 rpt_add('IPython version: %s \n\n' % release.version)
170 rpt_add('BZR revision : %s \n\n' % release.revision)
170 rpt_add('BZR revision : %s \n\n' % release.revision)
171 rpt_add('Platform info : os.name -> %s, sys.platform -> %s' %
171 rpt_add('Platform info : os.name -> %s, sys.platform -> %s' %
172 (os.name,sys.platform) )
172 (os.name,sys.platform) )
173 rpt_add(sec_sep+'Current user configuration structure:\n\n')
173 rpt_add(sec_sep+'Current user configuration structure:\n\n')
174 rpt_add(pformat(self.IP.dict()))
174 rpt_add(pformat(self.IP.dict()))
175 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
175 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
176 try:
176 try:
177 rpt_add(sec_sep+"History of session input:")
177 rpt_add(sec_sep+"History of session input:")
178 for line in self.IP.user_ns['_ih']:
178 for line in self.IP.user_ns['_ih']:
179 rpt_add(line)
179 rpt_add(line)
180 rpt_add('\n*** Last line of input (may not be in above history):\n')
180 rpt_add('\n*** Last line of input (may not be in above history):\n')
181 rpt_add(self.IP._last_input_line+'\n')
181 rpt_add(self.IP._last_input_line+'\n')
182 except:
182 except:
183 pass
183 pass
184
184
185 return ''.join(report)
185 return ''.join(report)
186
186
187 class IPythonCrashHandler(CrashHandler):
187 class IPythonCrashHandler(CrashHandler):
188 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
188 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
189
189
190 def __init__(self,IP):
190 def __init__(self,IP):
191
191
192 # Set here which of the IPython authors should be listed as contact
192 # Set here which of the IPython authors should be listed as contact
193 AUTHOR_CONTACT = 'Fernando'
193 AUTHOR_CONTACT = 'Fernando'
194
194
195 # Set argument defaults
195 # Set argument defaults
196 app_name = 'IPython'
196 app_name = 'IPython'
197 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
197 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
198 contact_name,contact_email = release.authors[AUTHOR_CONTACT][:2]
198 contact_name,contact_email = release.authors[AUTHOR_CONTACT][:2]
199 crash_report_fname = 'IPython_crash_report.txt'
199 crash_report_fname = 'IPython_crash_report.txt'
200 # Call parent constructor
200 # Call parent constructor
201 CrashHandler.__init__(self,IP,app_name,contact_name,contact_email,
201 CrashHandler.__init__(self,IP,app_name,contact_name,contact_email,
202 bug_tracker,crash_report_fname)
202 bug_tracker,crash_report_fname)
203
203
204 def make_report(self,traceback):
204 def make_report(self,traceback):
205 """Return a string containing a crash report."""
205 """Return a string containing a crash report."""
206
206
207 sec_sep = '\n\n'+'*'*75+'\n\n'
207 sec_sep = '\n\n'+'*'*75+'\n\n'
208
208
209 report = []
209 report = []
210 rpt_add = report.append
210 rpt_add = report.append
211
211
212 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
212 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
213 rpt_add('IPython version: %s \n\n' % release.version)
213 rpt_add('IPython version: %s \n\n' % release.version)
214 rpt_add('BZR revision : %s \n\n' % release.revision)
214 rpt_add('BZR revision : %s \n\n' % release.revision)
215 rpt_add('Platform info : os.name -> %s, sys.platform -> %s' %
215 rpt_add('Platform info : os.name -> %s, sys.platform -> %s' %
216 (os.name,sys.platform) )
216 (os.name,sys.platform) )
217 rpt_add(sec_sep+'Current user configuration structure:\n\n')
217 rpt_add(sec_sep+'Current user configuration structure:\n\n')
218 # rpt_add(pformat(self.IP.dict()))
218 # rpt_add(pformat(self.IP.dict()))
219 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
219 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
220 try:
220 try:
221 rpt_add(sec_sep+"History of session input:")
221 rpt_add(sec_sep+"History of session input:")
222 for line in self.IP.user_ns['_ih']:
222 for line in self.IP.user_ns['_ih']:
223 rpt_add(line)
223 rpt_add(line)
224 rpt_add('\n*** Last line of input (may not be in above history):\n')
224 rpt_add('\n*** Last line of input (may not be in above history):\n')
225 rpt_add(self.IP._last_input_line+'\n')
225 rpt_add(self.IP._last_input_line+'\n')
226 except:
226 except:
227 pass
227 pass
228
228
229 return ''.join(report)
229 return ''.join(report)
General Comments 0
You need to be logged in to leave comments. Login now