Skip to content

Commit f037771

Browse files
committed
Fix handling of chained exceptions
1 parent a6c3ccc commit f037771

File tree

3 files changed

+57
-2
lines changed

3 files changed

+57
-2
lines changed

demo_chained_exceptions.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import stackprinter
2+
stackprinter.set_excepthook()
3+
4+
5+
def bomb(msg):
6+
try:
7+
raise Exception(msg)
8+
except Exception as e:
9+
raise Exception(msg+'_b') from e
10+
11+
try:
12+
bomb('1')
13+
except Exception as e:
14+
try:
15+
raise Exception('2') from e
16+
except:
17+
bomb('3')
18+

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
setuptools.setup(
77
python_requires=">=3.4",
88
name="stackprinter",
9-
version="0.2.1",
9+
version="0.2.2",
1010
author="cknd",
1111
author_email="[email protected]",
1212
description="Debug-friendly stack traces, with variable values and semantic highlighting",

stackprinter/formatting.py

+38-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,44 @@ def format_exc_info(etype, evalue, tb, style='plaintext', add_summary='auto',
124124
125125
keyword args like stackprinter.format()
126126
"""
127+
msg = ''
127128
try:
129+
# First, recursively format any chained exceptions (exceptions during whose handling
130+
# the given one happened).
131+
# TODO: refactor this whole messy function to return a more... structured datastructure
132+
# before assembling a string, so that e.g. a summary of the whole chain can be shown at
133+
# the end.
134+
context = getattr(evalue, '__context__', None)
135+
cause = getattr(evalue, '__cause__', None)
136+
suppress_context = getattr(evalue, '__suppress_context__', False)
137+
if cause:
138+
chained_exc = cause
139+
chain_hint = ("\n\nThe above exception was the direct cause "
140+
"of the following exception:\n\n")
141+
elif context and not suppress_context:
142+
chained_exc = context
143+
chain_hint = ("\n\nWhile handling the above exception, "
144+
"another exception occurred:\n\n")
145+
else:
146+
chained_exc = None
147+
148+
if chained_exc:
149+
msg += format_exc_info(chained_exc.__class__,
150+
chained_exc,
151+
chained_exc.__traceback__,
152+
style=style,
153+
add_summary=add_summary,
154+
reverse=reverse,
155+
**kwargs)
156+
157+
if style == 'plaintext':
158+
msg += chain_hint
159+
else:
160+
sc = getattr(colorschemes, style)
161+
clr = get_ansi_tpl(*sc.colors['exception_type'])
162+
msg += clr % chain_hint
163+
164+
# Now, actually do some formatting:
128165
msgs = []
129166
if tb:
130167
frameinfos = [ex.get_info(tb_) for tb_ in _walk_traceback(tb)]
@@ -148,7 +185,7 @@ def format_exc_info(etype, evalue, tb, style='plaintext', add_summary='auto',
148185
if reverse:
149186
msgs = reversed(msgs)
150187

151-
msg = ''.join(msgs)
188+
msg += ''.join(msgs)
152189

153190
except Exception as exc:
154191
our_tb = traceback.format_exception(exc.__class__,

0 commit comments

Comments
 (0)