33import linecache
44import sys
55import types
6+ import itertools
67
78__all__ = ['extract_stack' , 'extract_tb' , 'format_exception' ,
89 'format_exception_only' , 'format_list' , 'format_stack' ,
@@ -107,7 +108,32 @@ def extract_tb(tb, limit = None):
107108 return list
108109
109110
110- def print_exception (etype , value , tb , limit = None , file = None ):
111+ _cause_message = (
112+ "\n The above exception was the direct cause "
113+ "of the following exception:\n " )
114+
115+ _context_message = (
116+ "\n During handling of the above exception, "
117+ "another exception occurred:\n " )
118+
119+ def _iter_chain (exc , custom_tb = None , seen = None ):
120+ if seen is None :
121+ seen = set ()
122+ seen .add (exc )
123+ its = []
124+ cause = exc .__cause__
125+ context = exc .__context__
126+ if cause is not None and cause not in seen :
127+ its .append (_iter_chain (cause , None , seen ))
128+ its .append ([(_cause_message , None )])
129+ if context is not None and context is not cause and context not in seen :
130+ its .append (_iter_chain (context , None , seen ))
131+ its .append ([(_context_message , None )])
132+ its .append ([(exc , custom_tb or exc .__traceback__ )])
133+ return itertools .chain (* its )
134+
135+
136+ def print_exception (etype , value , tb , limit = None , file = None , chain = True ):
111137 """Print exception up to 'limit' stack trace entries from 'tb' to 'file'.
112138
113139 This differs from print_tb() in the following ways: (1) if
@@ -120,15 +146,23 @@ def print_exception(etype, value, tb, limit=None, file=None):
120146 """
121147 if file is None :
122148 file = sys .stderr
123- if tb :
124- _print (file , 'Traceback (most recent call last):' )
125- print_tb (tb , limit , file )
126- lines = format_exception_only (etype , value )
127- for line in lines [:- 1 ]:
128- _print (file , line , ' ' )
129- _print (file , lines [- 1 ], '' )
130-
131- def format_exception (etype , value , tb , limit = None ):
149+ if chain :
150+ values = _iter_chain (value , tb )
151+ else :
152+ values = [(value , tb )]
153+ for value , tb in values :
154+ if isinstance (value , str ):
155+ _print (file , value )
156+ continue
157+ if tb :
158+ _print (file , 'Traceback (most recent call last):' )
159+ print_tb (tb , limit , file )
160+ lines = format_exception_only (type (value ), value )
161+ for line in lines [:- 1 ]:
162+ _print (file , line , ' ' )
163+ _print (file , lines [- 1 ], '' )
164+
165+ def format_exception (etype , value , tb , limit = None , chain = True ):
132166 """Format a stack trace and the exception information.
133167
134168 The arguments have the same meaning as the corresponding arguments
@@ -137,12 +171,19 @@ def format_exception(etype, value, tb, limit = None):
137171 these lines are concatenated and printed, exactly the same text is
138172 printed as does print_exception().
139173 """
140- if tb :
141- list = [ 'Traceback (most recent call last): \n ' ]
142- list = list + format_tb ( tb , limit )
174+ list = []
175+ if chain :
176+ values = _iter_chain ( value , tb )
143177 else :
144- list = []
145- list = list + format_exception_only (etype , value )
178+ values = [(value , tb )]
179+ for value , tb in values :
180+ if isinstance (value , str ):
181+ list .append (value + '\n ' )
182+ continue
183+ if tb :
184+ list .append ('Traceback (most recent call last):\n ' )
185+ list .extend (format_tb (tb , limit ))
186+ list .extend (format_exception_only (type (value ), value ))
146187 return list
147188
148189def format_exception_only (etype , value ):
@@ -208,33 +249,34 @@ def _some_str(value):
208249 return '<unprintable %s object>' % type (value ).__name__
209250
210251
211- def print_exc (limit = None , file = None ):
252+ def print_exc (limit = None , file = None , chain = True ):
212253 """Shorthand for 'print_exception(*sys.exc_info(), limit, file)'."""
213254 if file is None :
214255 file = sys .stderr
215256 try :
216257 etype , value , tb = sys .exc_info ()
217- print_exception (etype , value , tb , limit , file )
258+ print_exception (etype , value , tb , limit , file , chain )
218259 finally :
219260 etype = value = tb = None
220261
221262
222- def format_exc (limit = None ):
263+ def format_exc (limit = None , chain = True ):
223264 """Like print_exc() but return a string."""
224265 try :
225266 etype , value , tb = sys .exc_info ()
226- return '' .join (format_exception (etype , value , tb , limit ))
267+ return '' .join (
268+ format_exception (etype , value , tb , limit , chain ))
227269 finally :
228270 etype = value = tb = None
229271
230272
231- def print_last (limit = None , file = None ):
273+ def print_last (limit = None , file = None , chain = True ):
232274 """This is a shorthand for 'print_exception(sys.last_type,
233275 sys.last_value, sys.last_traceback, limit, file)'."""
234276 if file is None :
235277 file = sys .stderr
236278 print_exception (sys .last_type , sys .last_value , sys .last_traceback ,
237- limit , file )
279+ limit , file , chain )
238280
239281
240282def print_stack (f = None , limit = None , file = None ):
0 commit comments