1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Print prettier and more detailed exceptions. Copied from Telemetry."""
12 from telemetry
.core
import exceptions
13 from telemetry
.core
import util
16 def PrintFormattedException(exception_class
=None, exception
=None, tb
=None,
18 assert bool(exception_class
) == bool(exception
) == bool(tb
), (
19 'Must specify all or none of exception_class, exception, and tb')
21 if not exception_class
:
22 exception_class
, exception
, tb
= sys
.exc_info()
24 if exception_class
== exceptions
.IntentionalException
:
27 def _GetFinalFrame(tb_level
):
28 while tb_level
.tb_next
:
29 tb_level
= tb_level
.tb_next
30 return tb_level
.tb_frame
32 processed_tb
= traceback
.extract_tb(tb
)
33 frame
= _GetFinalFrame(tb
)
34 exception_list
= traceback
.format_exception_only(exception_class
, exception
)
35 exception_string
= '\n'.join(l
.strip() for l
in exception_list
)
39 print >> sys
.stderr
, msg
41 _PrintFormattedTrace(processed_tb
, frame
, exception_string
)
44 def PrintFormattedFrame(frame
, exception_string
=None):
45 _PrintFormattedTrace(traceback
.extract_stack(frame
), frame
, exception_string
)
48 def _PrintFormattedTrace(processed_tb
, frame
, exception_string
=None):
49 """Prints an Exception in a more useful format than the default.
51 TODO(tonyg): Consider further enhancements. For instance:
52 - Report stacks to maintainers like depot_tools does.
53 - Add a debug flag to automatically start pdb upon exception.
57 # Format the traceback.
58 base_dir
= os
.path
.abspath(util
.GetChromiumSrcDir())
59 print >> sys
.stderr
, 'Traceback (most recent call last):'
60 for filename
, line
, function
, text
in processed_tb
:
61 filename
= os
.path
.abspath(filename
)
62 if filename
.startswith(base_dir
):
63 filename
= filename
[len(base_dir
)+1:]
64 print >> sys
.stderr
, ' %s at %s:%d' % (function
, filename
, line
)
65 print >> sys
.stderr
, ' %s' % text
67 # Format the exception.
69 print >> sys
.stderr
, exception_string
72 local_variables
= [(variable
, value
) for variable
, value
in
73 frame
.f_locals
.iteritems() if variable
!= 'self']
75 print >> sys
.stderr
, 'Locals:'
77 longest_variable
= max(len(v
) for v
, _
in local_variables
)
78 for variable
, value
in sorted(local_variables
):
80 possibly_truncated_value
= _AbbreviateMiddleOfString(value
, ' ... ', 1024)
81 truncation_indication
= ''
82 if len(possibly_truncated_value
) != len(value
):
83 truncation_indication
= ' (truncated)'
84 print >> sys
.stderr
, ' %s: %s%s' % (variable
.ljust(longest_variable
+ 1),
85 possibly_truncated_value
,
86 truncation_indication
)
88 print >> sys
.stderr
, ' No locals!'
94 def _AbbreviateMiddleOfString(target
, middle
, max_length
):
96 raise ValueError('Must provide positive max_length')
97 if len(middle
) > max_length
:
98 raise ValueError('middle must not be greater than max_length')
100 if len(target
) <= max_length
:
102 half_length
= (max_length
- len(middle
)) / 2.
103 return (target
[:int(math
.floor(half_length
))] + middle
+
104 target
[-int(math
.ceil(half_length
)):])