##// END OF EJS Templates
Make crash handler tell users to email more detail...
Samuel Lelièvre -
Show More
@@ -1,225 +1,225 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-2011 The IPython Development Team
12 # Copyright (C) 2008-2011 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 import traceback
24 import traceback
25 from pprint import pformat
25 from pprint import pformat
26
26
27 from IPython.core import ultratb
27 from IPython.core import ultratb
28 from IPython.core.release import author_email
28 from IPython.core.release import author_email
29 from IPython.utils.sysinfo import sys_info
29 from IPython.utils.sysinfo import sys_info
30 from IPython.utils.py3compat import input
30 from IPython.utils.py3compat import input
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Code
33 # Code
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 # Template for the user message.
36 # Template for the user message.
37 _default_message_template = """\
37 _default_message_template = """\
38 Oops, {app_name} crashed. We do our best to make it stable, but...
38 Oops, {app_name} crashed. We do our best to make it stable, but...
39
39
40 A crash report was automatically generated with the following information:
40 A crash report was automatically generated with the following information:
41 - A verbatim copy of the crash traceback.
41 - A verbatim copy of the crash traceback.
42 - A copy of your input history during this session.
42 - A copy of your input history during this session.
43 - Data on your current {app_name} configuration.
43 - Data on your current {app_name} configuration.
44
44
45 It was left in the file named:
45 It was left in the file named:
46 \t'{crash_report_fname}'
46 \t'{crash_report_fname}'
47 If you can email this file to the developers, the information in it will help
47 If you can email this file to the developers, the information in it will help
48 them in understanding and correcting the problem.
48 them in understanding and correcting the problem.
49
49
50 You can mail it to: {contact_name} at {contact_email}
50 You can mail it to: {contact_name} at {contact_email}
51 with the subject '{app_name} Crash Report'.
51 with the subject '{app_name} Crash Report'.
52
52
53 If you want to do it now, the following command will work (under Unix):
53 If you want to do it now, the following command will work (under Unix):
54 mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname}
54 mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname}
55
55
56 Please also include in your email:
56 In your email, please also include information about:
57 - information about your operating system: is it Linux, macOS, Windows, other,
57 - The operating system under which the crash happened: Linux, macOS, Windows,
58 and which version, for example Ubuntu 16.04, macOS 10.13.2, Windows 10 Pro,
58 other, and which exact version (for example: Ubuntu 16.04.3, macOS 10.13.2,
59 is it 32-bit or 64-bit, ...
59 Windows 10 Pro), and whether it is 32-bit or 64-bit;
60 - information on how you installed {app_name}: from binary, compiled from source,
60 - How {app_name} was installed: using pip or conda, from GitHub, as part of
61 via Docker, on a virtual machine...
61 a Docker container, or other, providing more detail if possible;
62 - information on how to reproduce the crash: what exact sequence of instructions
62 - How to reproduce the crash: what exact sequence of instructions can one
63 can one input to get the same crash? Ideally, find a minimal yet complete
63 input to get the same crash? Ideally, find a minimal yet complete sequence
64 sequence of instructions that yields the crash.
64 of instructions that yields the crash.
65
65
66 To ensure accurate tracking of this issue, please file a report about it at:
66 To ensure accurate tracking of this issue, please file a report about it at:
67 {bug_tracker}
67 {bug_tracker}
68 """
68 """
69
69
70 _lite_message_template = """
70 _lite_message_template = """
71 If you suspect this is an IPython bug, please report it at:
71 If you suspect this is an IPython bug, please report it at:
72 https://github.com/ipython/ipython/issues
72 https://github.com/ipython/ipython/issues
73 or send an email to the mailing list at {email}
73 or send an email to the mailing list at {email}
74
74
75 You can print a more detailed traceback right now with "%tb", or use "%debug"
75 You can print a more detailed traceback right now with "%tb", or use "%debug"
76 to interactively debug it.
76 to interactively debug it.
77
77
78 Extra-detailed tracebacks for bug-reporting purposes can be enabled via:
78 Extra-detailed tracebacks for bug-reporting purposes can be enabled via:
79 {config}Application.verbose_crash=True
79 {config}Application.verbose_crash=True
80 """
80 """
81
81
82
82
83 class CrashHandler(object):
83 class CrashHandler(object):
84 """Customizable crash handlers for IPython applications.
84 """Customizable crash handlers for IPython applications.
85
85
86 Instances of this class provide a :meth:`__call__` method which can be
86 Instances of this class provide a :meth:`__call__` method which can be
87 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
87 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
88
88
89 def __call__(self, etype, evalue, etb)
89 def __call__(self, etype, evalue, etb)
90 """
90 """
91
91
92 message_template = _default_message_template
92 message_template = _default_message_template
93 section_sep = '\n\n'+'*'*75+'\n\n'
93 section_sep = '\n\n'+'*'*75+'\n\n'
94
94
95 def __init__(self, app, contact_name=None, contact_email=None,
95 def __init__(self, app, contact_name=None, contact_email=None,
96 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
96 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
97 """Create a new crash handler
97 """Create a new crash handler
98
98
99 Parameters
99 Parameters
100 ----------
100 ----------
101 app : Application
101 app : Application
102 A running :class:`Application` instance, which will be queried at
102 A running :class:`Application` instance, which will be queried at
103 crash time for internal information.
103 crash time for internal information.
104
104
105 contact_name : str
105 contact_name : str
106 A string with the name of the person to contact.
106 A string with the name of the person to contact.
107
107
108 contact_email : str
108 contact_email : str
109 A string with the email address of the contact.
109 A string with the email address of the contact.
110
110
111 bug_tracker : str
111 bug_tracker : str
112 A string with the URL for your project's bug tracker.
112 A string with the URL for your project's bug tracker.
113
113
114 show_crash_traceback : bool
114 show_crash_traceback : bool
115 If false, don't print the crash traceback on stderr, only generate
115 If false, don't print the crash traceback on stderr, only generate
116 the on-disk report
116 the on-disk report
117
117
118 Non-argument instance attributes:
118 Non-argument instance attributes:
119
119
120 These instances contain some non-argument attributes which allow for
120 These instances contain some non-argument attributes which allow for
121 further customization of the crash handler's behavior. Please see the
121 further customization of the crash handler's behavior. Please see the
122 source for further details.
122 source for further details.
123 """
123 """
124 self.crash_report_fname = "Crash_report_%s.txt" % app.name
124 self.crash_report_fname = "Crash_report_%s.txt" % app.name
125 self.app = app
125 self.app = app
126 self.call_pdb = call_pdb
126 self.call_pdb = call_pdb
127 #self.call_pdb = True # dbg
127 #self.call_pdb = True # dbg
128 self.show_crash_traceback = show_crash_traceback
128 self.show_crash_traceback = show_crash_traceback
129 self.info = dict(app_name = app.name,
129 self.info = dict(app_name = app.name,
130 contact_name = contact_name,
130 contact_name = contact_name,
131 contact_email = contact_email,
131 contact_email = contact_email,
132 bug_tracker = bug_tracker,
132 bug_tracker = bug_tracker,
133 crash_report_fname = self.crash_report_fname)
133 crash_report_fname = self.crash_report_fname)
134
134
135
135
136 def __call__(self, etype, evalue, etb):
136 def __call__(self, etype, evalue, etb):
137 """Handle an exception, call for compatible with sys.excepthook"""
137 """Handle an exception, call for compatible with sys.excepthook"""
138
138
139 # do not allow the crash handler to be called twice without reinstalling it
139 # do not allow the crash handler to be called twice without reinstalling it
140 # this prevents unlikely errors in the crash handling from entering an
140 # this prevents unlikely errors in the crash handling from entering an
141 # infinite loop.
141 # infinite loop.
142 sys.excepthook = sys.__excepthook__
142 sys.excepthook = sys.__excepthook__
143
143
144 # Report tracebacks shouldn't use color in general (safer for users)
144 # Report tracebacks shouldn't use color in general (safer for users)
145 color_scheme = 'NoColor'
145 color_scheme = 'NoColor'
146
146
147 # Use this ONLY for developer debugging (keep commented out for release)
147 # Use this ONLY for developer debugging (keep commented out for release)
148 #color_scheme = 'Linux' # dbg
148 #color_scheme = 'Linux' # dbg
149 try:
149 try:
150 rptdir = self.app.ipython_dir
150 rptdir = self.app.ipython_dir
151 except:
151 except:
152 rptdir = os.getcwd()
152 rptdir = os.getcwd()
153 if rptdir is None or not os.path.isdir(rptdir):
153 if rptdir is None or not os.path.isdir(rptdir):
154 rptdir = os.getcwd()
154 rptdir = os.getcwd()
155 report_name = os.path.join(rptdir,self.crash_report_fname)
155 report_name = os.path.join(rptdir,self.crash_report_fname)
156 # write the report filename into the instance dict so it can get
156 # write the report filename into the instance dict so it can get
157 # properly expanded out in the user message template
157 # properly expanded out in the user message template
158 self.crash_report_fname = report_name
158 self.crash_report_fname = report_name
159 self.info['crash_report_fname'] = report_name
159 self.info['crash_report_fname'] = report_name
160 TBhandler = ultratb.VerboseTB(
160 TBhandler = ultratb.VerboseTB(
161 color_scheme=color_scheme,
161 color_scheme=color_scheme,
162 long_header=1,
162 long_header=1,
163 call_pdb=self.call_pdb,
163 call_pdb=self.call_pdb,
164 )
164 )
165 if self.call_pdb:
165 if self.call_pdb:
166 TBhandler(etype,evalue,etb)
166 TBhandler(etype,evalue,etb)
167 return
167 return
168 else:
168 else:
169 traceback = TBhandler.text(etype,evalue,etb,context=31)
169 traceback = TBhandler.text(etype,evalue,etb,context=31)
170
170
171 # print traceback to screen
171 # print traceback to screen
172 if self.show_crash_traceback:
172 if self.show_crash_traceback:
173 print(traceback, file=sys.stderr)
173 print(traceback, file=sys.stderr)
174
174
175 # and generate a complete report on disk
175 # and generate a complete report on disk
176 try:
176 try:
177 report = open(report_name,'w')
177 report = open(report_name,'w')
178 except:
178 except:
179 print('Could not create crash report on disk.', file=sys.stderr)
179 print('Could not create crash report on disk.', file=sys.stderr)
180 return
180 return
181
181
182 # Inform user on stderr of what happened
182 # Inform user on stderr of what happened
183 print('\n'+'*'*70+'\n', file=sys.stderr)
183 print('\n'+'*'*70+'\n', file=sys.stderr)
184 print(self.message_template.format(**self.info), file=sys.stderr)
184 print(self.message_template.format(**self.info), file=sys.stderr)
185
185
186 # Construct report on disk
186 # Construct report on disk
187 report.write(self.make_report(traceback))
187 report.write(self.make_report(traceback))
188 report.close()
188 report.close()
189 input("Hit <Enter> to quit (your terminal may close):")
189 input("Hit <Enter> to quit (your terminal may close):")
190
190
191 def make_report(self,traceback):
191 def make_report(self,traceback):
192 """Return a string containing a crash report."""
192 """Return a string containing a crash report."""
193
193
194 sec_sep = self.section_sep
194 sec_sep = self.section_sep
195
195
196 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
196 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
197 rpt_add = report.append
197 rpt_add = report.append
198 rpt_add(sys_info())
198 rpt_add(sys_info())
199
199
200 try:
200 try:
201 config = pformat(self.app.config)
201 config = pformat(self.app.config)
202 rpt_add(sec_sep)
202 rpt_add(sec_sep)
203 rpt_add('Application name: %s\n\n' % self.app_name)
203 rpt_add('Application name: %s\n\n' % self.app_name)
204 rpt_add('Current user configuration structure:\n\n')
204 rpt_add('Current user configuration structure:\n\n')
205 rpt_add(config)
205 rpt_add(config)
206 except:
206 except:
207 pass
207 pass
208 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
208 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
209
209
210 return ''.join(report)
210 return ''.join(report)
211
211
212
212
213 def crash_handler_lite(etype, evalue, tb):
213 def crash_handler_lite(etype, evalue, tb):
214 """a light excepthook, adding a small message to the usual traceback"""
214 """a light excepthook, adding a small message to the usual traceback"""
215 traceback.print_exception(etype, evalue, tb)
215 traceback.print_exception(etype, evalue, tb)
216
216
217 from IPython.core.interactiveshell import InteractiveShell
217 from IPython.core.interactiveshell import InteractiveShell
218 if InteractiveShell.initialized():
218 if InteractiveShell.initialized():
219 # we are in a Shell environment, give %magic example
219 # we are in a Shell environment, give %magic example
220 config = "%config "
220 config = "%config "
221 else:
221 else:
222 # we are not in a shell, show generic config
222 # we are not in a shell, show generic config
223 config = "c."
223 config = "c."
224 print(_lite_message_template.format(email=author_email, config=config), file=sys.stderr)
224 print(_lite_message_template.format(email=author_email, config=config), file=sys.stderr)
225
225
General Comments 0
You need to be logged in to leave comments. Login now