|
|
|
"""sys.excepthook for IPython itself, leaves a detailed report on disk. |
|
|
|
Authors: |
|
|
|
* Fernando Perez |
|
* Brian E. Granger |
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import sys |
|
import traceback |
|
from pprint import pformat |
|
from pathlib import Path |
|
|
|
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 |
|
|
|
from typing import Optional |
|
|
|
|
|
|
|
|
|
|
|
|
|
_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: Optional[str] = None, |
|
contact_email: Optional[str] = None, |
|
bug_tracker: Optional[str] = None, |
|
show_crash_traceback: bool = True, |
|
call_pdb: bool = 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 |
|
call_pdb |
|
Whether to call pdb on crash |
|
|
|
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.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""" |
|
|
|
|
|
|
|
|
|
sys.excepthook = sys.__excepthook__ |
|
|
|
|
|
color_scheme = 'NoColor' |
|
|
|
|
|
|
|
try: |
|
rptdir = self.app.ipython_dir |
|
except: |
|
rptdir = Path.cwd() |
|
if rptdir is None or not Path.is_dir(rptdir): |
|
rptdir = Path.cwd() |
|
report_name = rptdir / self.crash_report_fname |
|
|
|
|
|
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) |
|
|
|
|
|
if self.show_crash_traceback: |
|
print(traceback, file=sys.stderr) |
|
|
|
|
|
try: |
|
report = open(report_name, "w", encoding="utf-8") |
|
except: |
|
print('Could not create crash report on disk.', file=sys.stderr) |
|
return |
|
|
|
with report: |
|
|
|
print('\n'+'*'*70+'\n', file=sys.stderr) |
|
print(self.message_template.format(**self.info), file=sys.stderr) |
|
|
|
|
|
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(): |
|
|
|
config = "%config " |
|
else: |
|
|
|
config = "c." |
|
print(_lite_message_template.format(email=author_email, config=config, version=version), file=sys.stderr) |
|
|
|
|