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