##// END OF EJS Templates
More work on the crash handler....
Brian Granger -
Show More
@@ -119,14 +119,13 b' class Application(object):'
119 argv = None
119 argv = None
120 #: extra arguments computed by the command-line loader
120 #: extra arguments computed by the command-line loader
121 extra_args = None
121 extra_args = None
122 #: The class to use as the crash handler.
123 crash_handler_class = crashhandler.CrashHandler
122
124
123 # Private attributes
125 # Private attributes
124 _exiting = False
126 _exiting = False
125 _initialized = False
127 _initialized = False
126
128
127 # Class choices for things that will be instantiated at runtime.
128 _CrashHandler = crashhandler.CrashHandler
129
130 def __init__(self, argv=None):
129 def __init__(self, argv=None):
131 self.argv = sys.argv[1:] if argv is None else argv
130 self.argv = sys.argv[1:] if argv is None else argv
132 self.init_logger()
131 self.init_logger()
@@ -217,7 +216,7 b' class Application(object):'
217
216
218 def create_crash_handler(self):
217 def create_crash_handler(self):
219 """Create a crash handler, typically setting sys.excepthook to it."""
218 """Create a crash handler, typically setting sys.excepthook to it."""
220 self.crash_handler = self._CrashHandler(self, self.name)
219 self.crash_handler = self.crash_handler_class(self)
221 sys.excepthook = self.crash_handler
220 sys.excepthook = self.crash_handler
222
221
223 def create_default_config(self):
222 def create_default_config(self):
@@ -430,15 +429,6 b' class Application(object):'
430 # Utility methods
429 # Utility methods
431 #-------------------------------------------------------------------------
430 #-------------------------------------------------------------------------
432
431
433 def abort(self):
434 """Abort the starting of the application."""
435 if self._exiting:
436 pass
437 else:
438 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
439 self._exiting = True
440 sys.exit(1)
441
442 def exit(self, exit_status=0):
432 def exit(self, exit_status=0):
443 if self._exiting:
433 if self._exiting:
444 pass
434 pass
@@ -447,17 +437,13 b' class Application(object):'
447 self._exiting = True
437 self._exiting = True
448 sys.exit(exit_status)
438 sys.exit(exit_status)
449
439
450 def attempt(self, func, action='abort'):
440 def attempt(self, func):
451 try:
441 try:
452 func()
442 func()
453 except SystemExit:
443 except SystemExit:
454 raise
444 raise
455 except:
445 except:
456 if action == 'abort':
457 self.log.critical("Aborting application: %s" % self.name,
446 self.log.critical("Aborting application: %s" % self.name,
458 exc_info=True)
447 exc_info=True)
459 self.abort()
460 raise
461 elif action == 'exit':
462 self.exit(0)
448 self.exit(0)
463
449
@@ -1,74 +1,94 b''
1 # -*- coding: 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
5
5 Authors
6 * Fernando Perez
6 -------
7 * Brian E. Granger
7 - Fernando Perez <Fernando.Perez@berkeley.edu>
8 """
8 """
9
9
10 #*****************************************************************************
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2008-2009 The IPython Development Team
12 # 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
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 # Imports
20 #-----------------------------------------------------------------------------
20
21
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
27 from IPython.core import release
28 from IPython.core import ultratb
26 from IPython.core import ultratb
27 from IPython.external.Itpl import itpl
29 from IPython.utils.sysinfo import sys_info
28 from IPython.utils.sysinfo import sys_info
30
29
31 from IPython.external.Itpl import itpl
30 #-----------------------------------------------------------------------------
31 # Code
32 #-----------------------------------------------------------------------------
32
33
33 #****************************************************************************
34 # Template for the user message.
35 _default_message_template = """\
36 Oops, $self.app_name crashed. We do our best to make it stable, but...
34
37
35 class CrashHandler(object):
38 A crash report was automatically generated with the following information:
36 """Customizable crash handlers for IPython-based systems.
39 - A verbatim copy of the crash traceback.
40 - A copy of your input history during this session.
41 - Data on your current $self.app_name configuration.
37
42
38 Instances of this class provide a __call__ method which can be used as a
43 It was left in the file named:
39 sys.excepthook, i.e., the __call__ signature is:
44 \t'$self.crash_report_fname'
45 If you can email this file to the developers, the information in it will help
46 them in understanding and correcting the problem.
40
47
41 def __call__(self,etype, evalue, etb)
48 You can mail it to: $self.contact_name at $self.contact_email
49 with the subject '$self.app_name Crash Report'.
50
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
42
53
54 To ensure accurate tracking of this issue, please file a report about it at:
55 $self.bug_tracker
43 """
56 """
44
57
45 def __init__(self,app, app_name, contact_name=None, contact_email=None,
46 bug_tracker=None, crash_report_fname='CrashReport.txt',
47 show_crash_traceback=True, call_pdb=False):
48 """New crash handler.
49
58
50 Inputs:
59 class CrashHandler(object):
60 """Customizable crash handlers for IPython applications.
51
61
52 - app: a running application instance, which will be queried at crash
62 Instances of this class provide a :meth:`__call__` method which can be
53 time for internal information.
63 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
54
64
55 - app_name: a string containing the name of your application.
65 def __call__(self, etype, evalue, etb)
66 """
56
67
57 - contact_name: a string with the name of the person to contact.
68 message_template = _default_message_template
58
69
59 - contact_email: a string with the email address of the contact.
70 def __init__(self, app, contact_name=None, contact_email=None,
71 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
72 """Create a new crash handler
60
73
61 - bug_tracker: a string with the URL for your project's bug tracker.
74 Parameters
75 ----------
76 app : Application
77 A running :class:`Application` instance, which will be queried at
78 crash time for internal information.
62
79
63 - crash_report_fname: a string with the filename for the crash report
80 contact_name : str
64 to be saved in. These reports are left in the ipython user directory
81 A string with the name of the person to contact.
65 as determined by the running IPython instance.
66
82
67 Optional inputs:
83 contact_email : str
84 A string with the email address of the contact.
68
85
69 - show_crash_traceback(True): if false, don't print the crash
86 bug_tracker : str
70 traceback on stderr, only generate the on-disk report
87 A string with the URL for your project's bug tracker.
71
88
89 show_crash_traceback : bool
90 If false, don't print the crash traceback on stderr, only generate
91 the on-disk report
72
92
73 Non-argument instance attributes:
93 Non-argument instance attributes:
74
94
@@ -76,48 +96,17 b' class CrashHandler(object):'
76 further customization of the crash handler's behavior. Please see the
96 further customization of the crash handler's behavior. Please see the
77 source for further details.
97 source for further details.
78 """
98 """
79
80 # apply args into instance
81 self.app = app
99 self.app = app
82 self.app_name = app_name
100 self.app_name = self.app.name
83 self.contact_name = contact_name
101 self.contact_name = contact_name
84 self.contact_email = contact_email
102 self.contact_email = contact_email
85 self.bug_tracker = bug_tracker
103 self.bug_tracker = bug_tracker
86 self.crash_report_fname = crash_report_fname
104 self.crash_report_fname = "Crash_report_%s.txt" % self.app_name
87 self.show_crash_traceback = show_crash_traceback
105 self.show_crash_traceback = show_crash_traceback
88 self.section_sep = '\n\n'+'*'*75+'\n\n'
106 self.section_sep = '\n\n'+'*'*75+'\n\n'
89 self.call_pdb = call_pdb
107 self.call_pdb = call_pdb
90 #self.call_pdb = True # dbg
108 #self.call_pdb = True # dbg
91
109
92 # Hardcoded defaults, which can be overridden either by subclasses or
93 # at runtime for the instance.
94
95 # Template for the user message. Subclasses which completely override
96 # this, or user apps, can modify it to suit their tastes. It gets
97 # expanded using itpl, so calls of the kind $self.foo are valid.
98 self.user_message_template = """
99 Oops, $self.app_name crashed. We do our best to make it stable, but...
100
101 A crash report was automatically generated with the following information:
102 - A verbatim copy of the crash traceback.
103 - A copy of your input history during this session.
104 - Data on your current $self.app_name configuration.
105
106 It was left in the file named:
107 \t'$self.crash_report_fname'
108 If you can email this file to the developers, the information in it will help
109 them in understanding and correcting the problem.
110
111 You can mail it to: $self.contact_name at $self.contact_email
112 with the subject '$self.app_name Crash Report'.
113
114 If you want to do it now, the following command will work (under Unix):
115 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
116
117 To ensure accurate tracking of this issue, please file a report about it at:
118 $self.bug_tracker
119 """
120
121 def __call__(self,etype, evalue, etb):
110 def __call__(self, etype, evalue, etb):
122 """Handle an exception, call for compatible with sys.excepthook"""
111 """Handle an exception, call for compatible with sys.excepthook"""
123
112
@@ -137,7 +126,8 b' $self.bug_tracker'
137 # write the report filename into the instance dict so it can get
126 # write the report filename into the instance dict so it can get
138 # properly expanded out in the user message template
127 # properly expanded out in the user message template
139 self.crash_report_fname = report_name
128 self.crash_report_fname = report_name
140 TBhandler = ultratb.VerboseTB(color_scheme=color_scheme,
129 TBhandler = ultratb.VerboseTB(
130 color_scheme=color_scheme,
141 long_header=1,
131 long_header=1,
142 call_pdb=self.call_pdb,
132 call_pdb=self.call_pdb,
143 )
133 )
@@ -159,7 +149,7 b' $self.bug_tracker'
159 return
149 return
160
150
161 # Inform user on stderr of what happened
151 # Inform user on stderr of what happened
162 msg = itpl('\n'+'*'*70+'\n'+self.user_message_template)
152 msg = itpl('\n'+'*'*70+'\n'+self.message_template)
163 print >> sys.stderr, msg
153 print >> sys.stderr, msg
164
154
165 # Construct report on disk
155 # Construct report on disk
@@ -178,7 +168,9 b' $self.bug_tracker'
178
168
179 try:
169 try:
180 config = pformat(self.app.config)
170 config = pformat(self.app.config)
181 rpt_add(sec_sep+'Current user configuration structure:\n\n')
171 rpt_add(sec_sep)
172 rpt_add('Application name: %s\n\n' % self.app_name)
173 rpt_add('Current user configuration structure:\n\n')
182 rpt_add(config)
174 rpt_add(config)
183 except:
175 except:
184 pass
176 pass
@@ -186,39 +178,3 b' $self.bug_tracker'
186
178
187 return ''.join(report)
179 return ''.join(report)
188
180
189
190 class IPythonCrashHandler(CrashHandler):
191 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
192
193 def __init__(self, app, app_name='IPython'):
194
195 # Set here which of the IPython authors should be listed as contact
196 AUTHOR_CONTACT = 'Fernando'
197
198 # Set argument defaults
199 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
200 contact_name,contact_email = release.authors[AUTHOR_CONTACT][:2]
201 crash_report_fname = 'IPython_crash_report.txt'
202 # Call parent constructor
203 CrashHandler.__init__(self,app,app_name,contact_name,contact_email,
204 bug_tracker,crash_report_fname)
205
206 def make_report(self,traceback):
207 """Return a string containing a crash report."""
208
209 sec_sep = self.section_sep
210 # Start with parent report
211 report = [super(IPythonCrashHandler, self).make_report(traceback)]
212 # Add interactive-specific info we may have
213 rpt_add = report.append
214 try:
215 rpt_add(sec_sep+"History of session input:")
216 for line in self.app.shell.user_ns['_ih']:
217 rpt_add(line)
218 rpt_add('\n*** Last line of input (may not be in above history):\n')
219 rpt_add(self.app.shell._last_input_line+'\n')
220 except:
221 pass
222
223 return ''.join(report)
224
@@ -21,13 +21,15 b' Authors'
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24 from __future__ import absolute_import
25 from __future__ import absolute_import
25
26
26 import logging
27 import logging
27 import os
28 import os
28 import sys
29 import sys
29
30
30 from IPython.core import crashhandler
31 from IPython.core import release
32 from IPython.core.crashhandler import CrashHandler
31 from IPython.core.application import Application, BaseAppConfigLoader
33 from IPython.core.application import Application, BaseAppConfigLoader
32 from IPython.core.iplib import InteractiveShell
34 from IPython.core.iplib import InteractiveShell
33 from IPython.config.loader import (
35 from IPython.config.loader import (
@@ -317,6 +319,67 b' class IPAppConfigLoader(BaseAppConfigLoader):'
317
319
318
320
319 #-----------------------------------------------------------------------------
321 #-----------------------------------------------------------------------------
322 # Crash handler for this application
323 #-----------------------------------------------------------------------------
324
325
326 _message_template = """\
327 Oops, $self.app_name crashed. We do our best to make it stable, but...
328
329 A crash report was automatically generated with the following information:
330 - A verbatim copy of the crash traceback.
331 - A copy of your input history during this session.
332 - Data on your current $self.app_name configuration.
333
334 It was left in the file named:
335 \t'$self.crash_report_fname'
336 If you can email this file to the developers, the information in it will help
337 them in understanding and correcting the problem.
338
339 You can mail it to: $self.contact_name at $self.contact_email
340 with the subject '$self.app_name Crash Report'.
341
342 If you want to do it now, the following command will work (under Unix):
343 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
344
345 To ensure accurate tracking of this issue, please file a report about it at:
346 $self.bug_tracker
347 """
348
349 class IPAppCrashHandler(CrashHandler):
350 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
351
352 message_template = _message_template
353
354 def __init__(self, app):
355 contact_name = release.authors['Fernando'][0]
356 contact_email = release.authors['Fernando'][1]
357 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
358 super(IPAppCrashHandler,self).__init__(
359 app, contact_name, contact_email, bug_tracker
360 )
361
362 def make_report(self,traceback):
363 """Return a string containing a crash report."""
364
365 sec_sep = self.section_sep
366 # Start with parent report
367 report = [super(IPAppCrashHandler, self).make_report(traceback)]
368 # Add interactive-specific info we may have
369 rpt_add = report.append
370 try:
371 rpt_add(sec_sep+"History of session input:")
372 for line in self.app.shell.user_ns['_ih']:
373 rpt_add(line)
374 rpt_add('\n*** Last line of input (may not be in above history):\n')
375 rpt_add(self.app.shell._last_input_line+'\n')
376 except:
377 pass
378
379 return ''.join(report)
380
381
382 #-----------------------------------------------------------------------------
320 # Main classes and functions
383 # Main classes and functions
321 #-----------------------------------------------------------------------------
384 #-----------------------------------------------------------------------------
322
385
@@ -327,9 +390,7 b' class IPythonApp(Application):'
327 usage = usage.cl_usage
390 usage = usage.cl_usage
328 command_line_loader = IPAppConfigLoader
391 command_line_loader = IPAppConfigLoader
329 config_file_name = default_config_file_name
392 config_file_name = default_config_file_name
330
393 crash_handler_class = IPAppCrashHandler
331 # Private and configuration attributes
332 _CrashHandler = crashhandler.IPythonCrashHandler
333
394
334 def create_default_config(self):
395 def create_default_config(self):
335 super(IPythonApp, self).create_default_config()
396 super(IPythonApp, self).create_default_config()
@@ -27,6 +27,8 b' from twisted.python import log'
27 from IPython.config.loader import PyFileConfigLoader
27 from IPython.config.loader import PyFileConfigLoader
28 from IPython.core.application import Application, BaseAppConfigLoader
28 from IPython.core.application import Application, BaseAppConfigLoader
29 from IPython.core.component import Component
29 from IPython.core.component import Component
30 from IPython.core.crashhandler import CrashHandler
31 from IPython.core import release
30 from IPython.utils.path import (
32 from IPython.utils.path import (
31 get_ipython_package_dir,
33 get_ipython_package_dir,
32 expand_path
34 expand_path
@@ -290,6 +292,47 b' class ClusterDirConfigLoader(BaseAppConfigLoader):'
290
292
291
293
292 #-----------------------------------------------------------------------------
294 #-----------------------------------------------------------------------------
295 # Crash handler for this application
296 #-----------------------------------------------------------------------------
297
298
299 _message_template = """\
300 Oops, $self.app_name crashed. We do our best to make it stable, but...
301
302 A crash report was automatically generated with the following information:
303 - A verbatim copy of the crash traceback.
304 - Data on your current $self.app_name configuration.
305
306 It was left in the file named:
307 \t'$self.crash_report_fname'
308 If you can email this file to the developers, the information in it will help
309 them in understanding and correcting the problem.
310
311 You can mail it to: $self.contact_name at $self.contact_email
312 with the subject '$self.app_name Crash Report'.
313
314 If you want to do it now, the following command will work (under Unix):
315 mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname
316
317 To ensure accurate tracking of this issue, please file a report about it at:
318 $self.bug_tracker
319 """
320
321 class ClusterDirCrashHandler(CrashHandler):
322 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
323
324 message_template = _message_template
325
326 def __init__(self, app):
327 contact_name = release.authors['Brian'][0]
328 contact_email = release.authors['Brian'][1]
329 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
330 super(ClusterDirCrashHandler,self).__init__(
331 app, contact_name, contact_email, bug_tracker
332 )
333
334
335 #-----------------------------------------------------------------------------
293 # Main application
336 # Main application
294 #-----------------------------------------------------------------------------
337 #-----------------------------------------------------------------------------
295
338
@@ -313,6 +356,7 b' class ApplicationWithClusterDir(Application):'
313 """
356 """
314
357
315 command_line_loader = ClusterDirConfigLoader
358 command_line_loader = ClusterDirConfigLoader
359 crash_handler_class = ClusterDirCrashHandler
316 auto_create_cluster_dir = True
360 auto_create_cluster_dir = True
317
361
318 def create_default_config(self):
362 def create_default_config(self):
General Comments 0
You need to be logged in to leave comments. Login now