##// END OF EJS Templates
Ensure crash handler works even when app isn't fully configured yet....
Fernando Perez -
Show More
@@ -1,180 +1,179 b''
1 # encoding: utf-8
1 # encoding: 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 Authors:
4 Authors:
5
5
6 * Fernando Perez
6 * Fernando Perez
7 * Brian E. Granger
7 * Brian E. Granger
8 """
8 """
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
11 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
12 # Copyright (C) 2008-2010 The IPython Development Team
12 # Copyright (C) 2008-2010 The IPython Development Team
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 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
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 from IPython.core import ultratb
26 from IPython.core import ultratb
27 from IPython.external.Itpl import itpl
27 from IPython.external.Itpl import itpl
28 from IPython.utils.sysinfo import sys_info
28 from IPython.utils.sysinfo import sys_info
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Code
31 # Code
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34 # Template for the user message.
34 # Template for the user message.
35 _default_message_template = """\
35 _default_message_template = """\
36 Oops, $self.app_name crashed. We do our best to make it stable, but...
36 Oops, $self.app_name crashed. We do our best to make it stable, but...
37
37
38 A crash report was automatically generated with the following information:
38 A crash report was automatically generated with the following information:
39 - A verbatim copy of the crash traceback.
39 - A verbatim copy of the crash traceback.
40 - A copy of your input history during this session.
40 - A copy of your input history during this session.
41 - Data on your current $self.app_name configuration.
41 - Data on your current $self.app_name configuration.
42
42
43 It was left in the file named:
43 It was left in the file named:
44 \t'$self.crash_report_fname'
44 \t'$self.crash_report_fname'
45 If you can email this file to the developers, the information in it will help
45 If you can email this file to the developers, the information in it will help
46 them in understanding and correcting the problem.
46 them in understanding and correcting the problem.
47
47
48 You can mail it to: $self.contact_name at $self.contact_email
48 You can mail it to: $self.contact_name at $self.contact_email
49 with the subject '$self.app_name Crash Report'.
49 with the subject '$self.app_name Crash Report'.
50
50
51 If you want to do it now, the following command will work (under Unix):
51 If you want to do it now, the following command will work (under Unix):
52 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
52 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
53
53
54 To ensure accurate tracking of this issue, please file a report about it at:
54 To ensure accurate tracking of this issue, please file a report about it at:
55 $self.bug_tracker
55 $self.bug_tracker
56 """
56 """
57
57
58
58
59 class CrashHandler(object):
59 class CrashHandler(object):
60 """Customizable crash handlers for IPython applications.
60 """Customizable crash handlers for IPython applications.
61
61
62 Instances of this class provide a :meth:`__call__` method which can be
62 Instances of this class provide a :meth:`__call__` method which can be
63 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
63 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
64
64
65 def __call__(self, etype, evalue, etb)
65 def __call__(self, etype, evalue, etb)
66 """
66 """
67
67
68 message_template = _default_message_template
68 message_template = _default_message_template
69
69
70 def __init__(self, app, contact_name=None, contact_email=None,
70 def __init__(self, app, contact_name=None, contact_email=None,
71 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
71 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
72 """Create a new crash handler
72 """Create a new crash handler
73
73
74 Parameters
74 Parameters
75 ----------
75 ----------
76 app : Application
76 app : Application
77 A running :class:`Application` instance, which will be queried at
77 A running :class:`Application` instance, which will be queried at
78 crash time for internal information.
78 crash time for internal information.
79
79
80 contact_name : str
80 contact_name : str
81 A string with the name of the person to contact.
81 A string with the name of the person to contact.
82
82
83 contact_email : str
83 contact_email : str
84 A string with the email address of the contact.
84 A string with the email address of the contact.
85
85
86 bug_tracker : str
86 bug_tracker : str
87 A string with the URL for your project's bug tracker.
87 A string with the URL for your project's bug tracker.
88
88
89 show_crash_traceback : bool
89 show_crash_traceback : bool
90 If false, don't print the crash traceback on stderr, only generate
90 If false, don't print the crash traceback on stderr, only generate
91 the on-disk report
91 the on-disk report
92
92
93 Non-argument instance attributes:
93 Non-argument instance attributes:
94
94
95 These instances contain some non-argument attributes which allow for
95 These instances contain some non-argument attributes which allow for
96 further customization of the crash handler's behavior. Please see the
96 further customization of the crash handler's behavior. Please see the
97 source for further details.
97 source for further details.
98 """
98 """
99 self.app = app
99 self.app = app
100 self.app_name = self.app.name
100 self.app_name = self.app.name
101 self.contact_name = contact_name
101 self.contact_name = contact_name
102 self.contact_email = contact_email
102 self.contact_email = contact_email
103 self.bug_tracker = bug_tracker
103 self.bug_tracker = bug_tracker
104 self.crash_report_fname = "Crash_report_%s.txt" % self.app_name
104 self.crash_report_fname = "Crash_report_%s.txt" % self.app_name
105 self.show_crash_traceback = show_crash_traceback
105 self.show_crash_traceback = show_crash_traceback
106 self.section_sep = '\n\n'+'*'*75+'\n\n'
106 self.section_sep = '\n\n'+'*'*75+'\n\n'
107 self.call_pdb = call_pdb
107 self.call_pdb = call_pdb
108 #self.call_pdb = True # dbg
108 #self.call_pdb = True # dbg
109
109
110 def __call__(self, etype, evalue, etb):
110 def __call__(self, etype, evalue, etb):
111 """Handle an exception, call for compatible with sys.excepthook"""
111 """Handle an exception, call for compatible with sys.excepthook"""
112
112
113 # Report tracebacks shouldn't use color in general (safer for users)
113 # Report tracebacks shouldn't use color in general (safer for users)
114 color_scheme = 'NoColor'
114 color_scheme = 'NoColor'
115
115
116 # Use this ONLY for developer debugging (keep commented out for release)
116 # Use this ONLY for developer debugging (keep commented out for release)
117 #color_scheme = 'Linux' # dbg
117 #color_scheme = 'Linux' # dbg
118
119 try:
118 try:
120 rptdir = self.app.ipython_dir
119 rptdir = self.app.ipython_dir
121 except:
120 except:
122 rptdir = os.getcwd()
121 rptdir = os.getcwd()
123 if not os.path.isdir(rptdir):
122 if rptdir is None or not os.path.isdir(rptdir):
124 rptdir = os.getcwd()
123 rptdir = os.getcwd()
125 report_name = os.path.join(rptdir,self.crash_report_fname)
124 report_name = os.path.join(rptdir,self.crash_report_fname)
126 # write the report filename into the instance dict so it can get
125 # write the report filename into the instance dict so it can get
127 # properly expanded out in the user message template
126 # properly expanded out in the user message template
128 self.crash_report_fname = report_name
127 self.crash_report_fname = report_name
129 TBhandler = ultratb.VerboseTB(
128 TBhandler = ultratb.VerboseTB(
130 color_scheme=color_scheme,
129 color_scheme=color_scheme,
131 long_header=1,
130 long_header=1,
132 call_pdb=self.call_pdb,
131 call_pdb=self.call_pdb,
133 )
132 )
134 if self.call_pdb:
133 if self.call_pdb:
135 TBhandler(etype,evalue,etb)
134 TBhandler(etype,evalue,etb)
136 return
135 return
137 else:
136 else:
138 traceback = TBhandler.text(etype,evalue,etb,context=31)
137 traceback = TBhandler.text(etype,evalue,etb,context=31)
139
138
140 # print traceback to screen
139 # print traceback to screen
141 if self.show_crash_traceback:
140 if self.show_crash_traceback:
142 print >> sys.stderr, traceback
141 print >> sys.stderr, traceback
143
142
144 # and generate a complete report on disk
143 # and generate a complete report on disk
145 try:
144 try:
146 report = open(report_name,'w')
145 report = open(report_name,'w')
147 except:
146 except:
148 print >> sys.stderr, 'Could not create crash report on disk.'
147 print >> sys.stderr, 'Could not create crash report on disk.'
149 return
148 return
150
149
151 # Inform user on stderr of what happened
150 # Inform user on stderr of what happened
152 msg = itpl('\n'+'*'*70+'\n'+self.message_template)
151 msg = itpl('\n'+'*'*70+'\n'+self.message_template)
153 print >> sys.stderr, msg
152 print >> sys.stderr, msg
154
153
155 # Construct report on disk
154 # Construct report on disk
156 report.write(self.make_report(traceback))
155 report.write(self.make_report(traceback))
157 report.close()
156 report.close()
158 raw_input("Hit <Enter> to quit this message (your terminal may close):")
157 raw_input("Hit <Enter> to quit this message (your terminal may close):")
159
158
160 def make_report(self,traceback):
159 def make_report(self,traceback):
161 """Return a string containing a crash report."""
160 """Return a string containing a crash report."""
162
161
163 sec_sep = self.section_sep
162 sec_sep = self.section_sep
164
163
165 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
164 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
166 rpt_add = report.append
165 rpt_add = report.append
167 rpt_add(sys_info())
166 rpt_add(sys_info())
168
167
169 try:
168 try:
170 config = pformat(self.app.config)
169 config = pformat(self.app.config)
171 rpt_add(sec_sep)
170 rpt_add(sec_sep)
172 rpt_add('Application name: %s\n\n' % self.app_name)
171 rpt_add('Application name: %s\n\n' % self.app_name)
173 rpt_add('Current user configuration structure:\n\n')
172 rpt_add('Current user configuration structure:\n\n')
174 rpt_add(config)
173 rpt_add(config)
175 except:
174 except:
176 pass
175 pass
177 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
176 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
178
177
179 return ''.join(report)
178 return ''.join(report)
180
179
General Comments 0
You need to be logged in to leave comments. Login now