aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/ipython/py3/IPython/core/crashhandler.py
blob: b39aac4c1f242849311350d8ad323200f25c6fd3 (plain) (blame)
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
# encoding: utf-8 
"""sys.excepthook for IPython itself, leaves a detailed report on disk. 
 
Authors: 
 
* Fernando Perez 
* Brian E. Granger 
""" 
 
#----------------------------------------------------------------------------- 
#  Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu> 
#  Copyright (C) 2008-2011  The IPython Development Team 
# 
#  Distributed under the terms of the BSD License.  The full license is in 
#  the file COPYING, distributed as part of this software. 
#----------------------------------------------------------------------------- 
 
#----------------------------------------------------------------------------- 
# Imports 
#----------------------------------------------------------------------------- 
 
import os 
import sys 
import traceback 
from pprint import pformat 
 
from IPython.core import ultratb 
from IPython.core.release import author_email 
from IPython.utils.sysinfo import sys_info 
from IPython.utils.py3compat import input 
 
from IPython.core.release import __version__ as version 
 
#----------------------------------------------------------------------------- 
# Code 
#----------------------------------------------------------------------------- 
 
# Template for the user message. 
_default_message_template = """\ 
Oops, {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 {app_name} configuration. 
 
It was left in the file named: 
\t'{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: {contact_name} at {contact_email} 
with the subject '{app_name} Crash Report'. 
 
If you want to do it now, the following command will work (under Unix): 
mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname} 
 
In your email, please also include information about: 
- The operating system under which the crash happened: Linux, macOS, Windows, 
  other, and which exact version (for example: Ubuntu 16.04.3, macOS 10.13.2, 
  Windows 10 Pro), and whether it is 32-bit or 64-bit; 
- How {app_name} was installed: using pip or conda, from GitHub, as part of 
  a Docker container, or other, providing more detail if possible; 
- How to reproduce the crash: what exact sequence of instructions can one 
  input to get the same crash? Ideally, find a minimal yet complete sequence 
  of instructions that yields the crash. 
 
To ensure accurate tracking of this issue, please file a report about it at: 
{bug_tracker} 
""" 
 
_lite_message_template = """ 
If you suspect this is an IPython {version} bug, please report it at: 
    https://github.com/ipython/ipython/issues 
or send an email to the mailing list at {email} 
 
You can print a more detailed traceback right now with "%tb", or use "%debug" 
to interactively debug it. 
 
Extra-detailed tracebacks for bug-reporting purposes can be enabled via: 
    {config}Application.verbose_crash=True 
""" 
 
 
class CrashHandler(object): 
    """Customizable crash handlers for IPython applications. 
 
    Instances of this class provide a :meth:`__call__` method which can be 
    used as a ``sys.excepthook``.  The :meth:`__call__` signature is:: 
 
        def __call__(self, etype, evalue, etb) 
    """ 
 
    message_template = _default_message_template 
    section_sep = '\n\n'+'*'*75+'\n\n' 
 
    def __init__(self, app, contact_name=None, contact_email=None, 
                 bug_tracker=None, show_crash_traceback=True, call_pdb=False): 
        """Create a new crash handler 
 
        Parameters 
        ---------- 
        app :  Application 
            A running :class:`Application` instance, which will be queried at 
            crash time for internal information. 
 
        contact_name : str 
            A string with the name of the person to contact. 
 
        contact_email : str 
            A string with the email address of the contact. 
 
        bug_tracker : str 
            A string with the URL for your project's bug tracker. 
 
        show_crash_traceback : bool 
            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. 
        """ 
        self.crash_report_fname = "Crash_report_%s.txt" % app.name 
        self.app = app 
        self.call_pdb = call_pdb 
        #self.call_pdb = True # dbg 
        self.show_crash_traceback = show_crash_traceback 
        self.info = dict(app_name = app.name, 
                    contact_name = contact_name, 
                    contact_email = contact_email, 
                    bug_tracker = bug_tracker, 
                    crash_report_fname = self.crash_report_fname) 
 
 
    def __call__(self, etype, evalue, etb): 
        """Handle an exception, call for compatible with sys.excepthook""" 
         
        # do not allow the crash handler to be called twice without reinstalling it 
        # this prevents unlikely errors in the crash handling from entering an 
        # infinite loop. 
        sys.excepthook = 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.app.ipython_dir 
        except: 
            rptdir = os.getcwd() 
        if rptdir is None or 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 
        self.info['crash_report_fname'] = report_name 
        TBhandler = ultratb.VerboseTB( 
            color_scheme=color_scheme, 
            long_header=1, 
            call_pdb=self.call_pdb, 
        ) 
        if self.call_pdb: 
            TBhandler(etype,evalue,etb) 
            return 
        else: 
            traceback = TBhandler.text(etype,evalue,etb,context=31) 
 
        # print traceback to screen 
        if self.show_crash_traceback: 
            print(traceback, file=sys.stderr) 
 
        # and generate a complete report on disk 
        try: 
            report = open(report_name,'w') 
        except: 
            print('Could not create crash report on disk.', file=sys.stderr) 
            return 
 
        with report: 
            # Inform user on stderr of what happened 
            print('\n'+'*'*70+'\n', file=sys.stderr) 
            print(self.message_template.format(**self.info), file=sys.stderr) 
 
            # Construct report on disk 
            report.write(self.make_report(traceback)) 
 
        input("Hit <Enter> to quit (your terminal may close):") 
 
    def make_report(self,traceback): 
        """Return a string containing a crash report.""" 
 
        sec_sep = self.section_sep 
 
        report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n'] 
        rpt_add = report.append 
        rpt_add(sys_info()) 
 
        try: 
            config = pformat(self.app.config) 
            rpt_add(sec_sep) 
            rpt_add('Application name: %s\n\n' % self.app_name) 
            rpt_add('Current user configuration structure:\n\n') 
            rpt_add(config) 
        except: 
            pass 
        rpt_add(sec_sep+'Crash traceback:\n\n' + traceback) 
 
        return ''.join(report) 
 
 
def crash_handler_lite(etype, evalue, tb): 
    """a light excepthook, adding a small message to the usual traceback""" 
    traceback.print_exception(etype, evalue, tb) 
     
    from IPython.core.interactiveshell import InteractiveShell 
    if InteractiveShell.initialized(): 
        # we are in a Shell environment, give %magic example 
        config = "%config " 
    else: 
        # we are not in a shell, show generic config 
        config = "c." 
    print(_lite_message_template.format(email=author_email, config=config, version=version), file=sys.stderr)