1
|
|
|
# -*- coding: utf-8 |
2
|
|
|
"""APIs to add suggestions to exceptions.""" |
3
|
|
|
from didyoumean_internal import add_suggestions_to_exception |
4
|
|
|
import functools |
5
|
|
|
import sys |
6
|
|
|
|
7
|
|
|
|
8
|
|
|
def didyoumean_decorator(func): |
9
|
|
|
"""Decorator to add suggestions to exceptions. |
10
|
|
|
|
11
|
|
|
To use it, decorate one of the functions called, for instance 'main()': |
12
|
|
|
@didyoumean_decorator |
13
|
|
|
def main(): |
14
|
|
|
some_code |
15
|
|
|
""" |
16
|
|
|
@functools.wraps(func) |
17
|
|
|
def decorated(*args, **kwargs): |
18
|
|
|
"""Function returned by the decorator.""" |
19
|
|
|
try: |
20
|
|
|
return func(*args, **kwargs) |
21
|
|
|
except: |
22
|
|
|
type_, value, traceback = sys.exc_info() |
23
|
|
|
add_suggestions_to_exception(type_, value, traceback) |
24
|
|
|
raise |
25
|
|
|
return decorated |
26
|
|
|
|
27
|
|
|
|
28
|
|
|
def didyoumean_postmortem(): |
29
|
|
|
"""Post postem function to add suggestions to last exception thrown. |
30
|
|
|
|
31
|
|
|
Add suggestions to last exception thrown (in interactive mode) and |
32
|
|
|
return it (which should print it). |
33
|
|
|
""" |
34
|
|
|
if hasattr(sys, 'last_type'): |
35
|
|
|
typ, val, trace = sys.last_type, sys.last_value, sys.last_traceback |
36
|
|
|
add_suggestions_to_exception(typ, val, trace) |
37
|
|
|
return val |
38
|
|
|
return None |
39
|
|
|
|
40
|
|
|
|
41
|
|
|
class didyoumean_contextmanager(object): |
42
|
|
|
"""Context manager to add suggestions to exceptions. |
43
|
|
|
|
44
|
|
|
To use it, create a context: |
45
|
|
|
with didyoumean_contextmanager(): |
46
|
|
|
some_code. |
47
|
|
|
""" |
48
|
|
|
|
49
|
|
|
def __enter__(self): |
50
|
|
|
"""Method called when entering the context manager. |
51
|
|
|
|
52
|
|
|
Not relevant here (does not do anything). |
53
|
|
|
""" |
54
|
|
|
pass |
55
|
|
|
|
56
|
|
|
def __exit__(self, type_, value, traceback): |
57
|
|
|
"""Method called when exiting the context manager. |
58
|
|
|
|
59
|
|
|
Add suggestions to the exception (if any). |
60
|
|
|
""" |
61
|
|
|
assert (type_ is None) == (value is None) |
62
|
|
|
if value is not None: |
63
|
|
|
if isinstance(value, type_): |
64
|
|
|
# Error is not re-raised as it is the caller's responsability |
65
|
|
|
# but the error is enhanced nonetheless |
66
|
|
|
add_suggestions_to_exception(type_, value, traceback) |
67
|
|
|
else: |
68
|
|
|
# Python 2.6 bug : http://bugs.python.org/issue7853 |
69
|
|
|
# Instead of having the exception, we have its representation |
70
|
|
|
# We can try to rebuild the exception, add suggestions to it |
71
|
|
|
# and re-raise it (re-raise shouldn't be done normally but it |
72
|
|
|
# is a dirty work-around for a dirty issue). |
73
|
|
|
if isinstance(value, str): |
74
|
|
|
value = type_(value) |
75
|
|
|
else: |
76
|
|
|
value = type_(*value) |
77
|
|
|
add_suggestions_to_exception(type_, value, traceback) |
78
|
|
|
raise value |
79
|
|
|
|
80
|
|
|
|
81
|
|
|
def didyoumean_hook(type_, value, traceback, prev_hook=sys.excepthook): |
82
|
|
|
"""Hook to be substituted to sys.excepthook to enhance exceptions.""" |
83
|
|
|
add_suggestions_to_exception(type_, value, traceback) |
84
|
|
|
return prev_hook(type_, value, traceback) |
85
|
|
|
|
86
|
|
|
|
87
|
|
|
def didyoumean_custom_exc(shell, etype, evalue, tb, tb_offset=None): |
88
|
|
|
"""Custom exception handler to replace the iPython one.""" |
89
|
|
|
add_suggestions_to_exception(etype, evalue, tb) |
90
|
|
|
return shell.showtraceback((etype, evalue, tb), tb_offset=tb_offset) |
91
|
|
|
|
92
|
|
|
|
93
|
|
|
def set_ipython_custom_exc(func): |
94
|
|
|
"""Try to set the custom exception handler for iPython.""" |
95
|
|
|
# https://mail.scipy.org/pipermail/ipython-dev/2012-April/008945.html |
96
|
|
|
# http://stackoverflow.com/questions/1261668/cannot-override-sys-excepthook |
97
|
|
|
try: |
98
|
|
|
get_ipython().set_custom_exc((Exception,), func) |
99
|
|
|
except NameError: |
100
|
|
|
pass # get_ipython does not exist - ignore |
101
|
|
|
|
102
|
|
|
|
103
|
|
|
def didyoumean_enablehook(): |
104
|
|
|
"""Function to set hooks to their custom value.""" |
105
|
|
|
sys.excepthook = didyoumean_hook |
106
|
|
|
set_ipython_custom_exc(didyoumean_custom_exc) |
107
|
|
|
|
108
|
|
|
|
109
|
|
|
def didyoumean_disablehook(): |
110
|
|
|
"""Function to set hooks to their normal value.""" |
111
|
|
|
sys.excepthook = sys.__excepthook__ |
112
|
|
|
set_ipython_custom_exc(None) |
113
|
|
|
|
114
|
|
|
# NOTE: It could be funny to have a magic command in Python |
115
|
|
|
# https://ipython.org/ipython-doc/dev/config/custommagics.html |
116
|
|
|
|