Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/IPython/CrashHandler.py
blob: d8388be059f01d17be31de2e1e6af943b9581ec2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# -*- coding: utf-8 -*-
"""sys.excepthook for IPython itself, leaves a detailed report on disk.


Authors
-------
- Fernando Perez <Fernando.Perez@berkeley.edu>
"""

#*****************************************************************************
#       Copyright (C) 2008-2009 The IPython Development Team
#       Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
#
#  Distributed under the terms of the BSD License.  The full license is in
#  the file COPYING, distributed as part of this software.
#*****************************************************************************

#****************************************************************************
# Required modules

# From the standard library
import os
import sys
from pprint import pprint,pformat

# Our own
from IPython import Release
from IPython import ultraTB
from IPython.ColorANSI import ColorScheme,ColorSchemeTable  # too long names
from IPython.Itpl import Itpl,itpl,printpl

from IPython.genutils import *

#****************************************************************************
class CrashHandler:
    """Customizable crash handlers for IPython-based systems.

    Instances of this class provide a __call__ method which can be used as a
    sys.excepthook, i.e., the __call__ signature is:

        def __call__(self,etype, evalue, etb)

    """

    def __init__(self,IP,app_name,contact_name,contact_email,
                 bug_tracker,crash_report_fname,
                 show_crash_traceback=True):
        """New crash handler.

        Inputs:

        - IP: a running IPython instance, which will be queried at crash time
        for internal information.

        - app_name: a string containing the name of your application.

        - contact_name: a string with the name of the person to contact.

        - contact_email: a string with the email address of the contact.

        - bug_tracker: a string with the URL for your project's bug tracker.

        - crash_report_fname: a string with the filename for the crash report
        to be saved in.  These reports are left in the ipython user directory
        as determined by the running IPython instance.

        Optional inputs:
        
        - show_crash_traceback(True): if false, don't print the crash
        traceback on stderr, only generate the on-disk report


        Non-argument instance attributes:

        These instances contain some non-argument attributes which allow for 
        further customization of the crash handler's behavior.  Please see the
        source for further details.
        """

        # apply args into instance
        self.IP = IP  # IPython instance
        self.app_name = app_name
        self.contact_name = contact_name
        self.contact_email = contact_email
        self.bug_tracker = bug_tracker
        self.crash_report_fname = crash_report_fname
        self.show_crash_traceback = show_crash_traceback
        
        # Hardcoded defaults, which can be overridden either by subclasses or
        # at runtime for the instance.

        # Template for the user message.  Subclasses which completely override
        # this, or user apps, can modify it to suit their tastes.  It gets
        # expanded using itpl, so calls of the kind $self.foo are valid.
        self.user_message_template = """
Oops, $self.app_name crashed. We do our best to make it stable, but...

A crash report was automatically generated with the following information:
  - A verbatim copy of the crash traceback.
  - A copy of your input history during this session.
  - Data on your current $self.app_name configuration.

It was left in the file named:
\t'$self.crash_report_fname'
If you can email this file to the developers, the information in it will help
them in understanding and correcting the problem.

You can mail it to: $self.contact_name at $self.contact_email
with the subject '$self.app_name Crash Report'.

If you want to do it now, the following command will work (under Unix):
mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname

To ensure accurate tracking of this issue, please file a report about it at:
$self.bug_tracker
"""

    def __call__(self,etype, evalue, etb):
        """Handle an exception, call for compatible with sys.excepthook"""

        # Report tracebacks shouldn't use color in general (safer for users)
        color_scheme = 'NoColor'

        # Use this ONLY for developer debugging (keep commented out for release)
        #color_scheme = 'Linux'   # dbg
        
        try:
            rptdir = self.IP.rc.ipythondir
        except:
            rptdir = os.getcwd()
        if not os.path.isdir(rptdir):
            rptdir = os.getcwd()
        report_name = os.path.join(rptdir,self.crash_report_fname)
        # write the report filename into the instance dict so it can get
        # properly expanded out in the user message template
        self.crash_report_fname = report_name
        TBhandler = ultraTB.VerboseTB(color_scheme=color_scheme,
                                           long_header=1)
        traceback = TBhandler.text(etype,evalue,etb,context=31)

        # print traceback to screen
        if self.show_crash_traceback:
            print >> sys.stderr, traceback

        # and generate a complete report on disk
        try:
            report = open(report_name,'w')
        except:
            print >> sys.stderr, 'Could not create crash report on disk.'
            return

        # Inform user on stderr of what happened
        msg = itpl('\n'+'*'*70+'\n'+self.user_message_template)
        print >> sys.stderr, msg

        # Construct report on disk
        report.write(self.make_report(traceback))
        report.close()
        raw_input("Press enter to exit:")

    def make_report(self,traceback):
        """Return a string containing a crash report."""

        sec_sep = '\n\n'+'*'*75+'\n\n'

        report = []
        rpt_add = report.append
        
        rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
        rpt_add('IPython version: %s \n\n' % Release.version)
        rpt_add('BZR revision   : %s \n\n' % Release.revision)
        rpt_add('Platform info  : os.name -> %s, sys.platform -> %s' %
                     (os.name,sys.platform) )
        rpt_add(sec_sep+'Current user configuration structure:\n\n')
        rpt_add(pformat(self.IP.rc.dict()))
        rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
        try:
            rpt_add(sec_sep+"History of session input:")
            for line in self.IP.user_ns['_ih']:
                rpt_add(line)
            rpt_add('\n*** Last line of input (may not be in above history):\n')
            rpt_add(self.IP._last_input_line+'\n')
        except:
            pass

        return ''.join(report)

class IPythonCrashHandler(CrashHandler):
    """sys.excepthook for IPython itself, leaves a detailed report on disk."""
    
    def __init__(self,IP):

        # Set here which of the IPython authors should be listed as contact
        AUTHOR_CONTACT = 'Fernando'
        
        # Set argument defaults
        app_name = 'IPython'
        bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
        contact_name,contact_email = Release.authors[AUTHOR_CONTACT][:2]
        crash_report_fname = 'IPython_crash_report.txt'
        # Call parent constructor
        CrashHandler.__init__(self,IP,app_name,contact_name,contact_email,
                              bug_tracker,crash_report_fname)

    def make_report(self,traceback):
        """Return a string containing a crash report."""

        sec_sep = '\n\n'+'*'*75+'\n\n'

        report = []
        rpt_add = report.append
        
        rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
        rpt_add('IPython version: %s \n\n' % Release.version)
        rpt_add('BZR revision   : %s \n\n' % Release.revision)
        rpt_add('Platform info  : os.name -> %s, sys.platform -> %s' %
                     (os.name,sys.platform) )
        rpt_add(sec_sep+'Current user configuration structure:\n\n')
        rpt_add(pformat(self.IP.rc.dict()))
        rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
        try:
            rpt_add(sec_sep+"History of session input:")
            for line in self.IP.user_ns['_ih']:
                rpt_add(line)
            rpt_add('\n*** Last line of input (may not be in above history):\n')
            rpt_add(self.IP._last_input_line+'\n')
        except:
            pass

        return ''.join(report)