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