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