Home | History | Annotate | Download | only in web-page-replay
      1 # Copyright 2015 Google Inc. All Rights Reserved.
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #      http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 
     15 import math
     16 import os
     17 import sys
     18 import traceback
     19 
     20 
     21 def PrintFormattedException(msg=None):
     22   exception_class, exception, tb = sys.exc_info()
     23 
     24   def _GetFinalFrame(tb_level):
     25     while tb_level.tb_next:
     26       tb_level = tb_level.tb_next
     27     return tb_level.tb_frame
     28 
     29   processed_tb = traceback.extract_tb(tb)
     30   frame = _GetFinalFrame(tb)
     31   exception_list = traceback.format_exception_only(exception_class, exception)
     32   exception_string = '\n'.join(l.strip() for l in exception_list)
     33 
     34   if msg:
     35     print >> sys.stderr
     36     print >> sys.stderr, msg
     37 
     38   _PrintFormattedTrace(processed_tb, frame, exception_string)
     39 
     40 def PrintFormattedFrame(frame, exception_string=None):
     41   _PrintFormattedTrace(traceback.extract_stack(frame), frame, exception_string)
     42 
     43 
     44 def _PrintFormattedTrace(processed_tb, frame, exception_string=None):
     45   """Prints an Exception in a more useful format than the default.
     46   """
     47   print >> sys.stderr
     48 
     49   # Format the traceback.
     50   base_dir = os.path.dirname(__file__)
     51   print >> sys.stderr, 'Traceback (most recent call last):'
     52   for filename, line, function, text in processed_tb:
     53     filename = os.path.abspath(filename)
     54     if filename.startswith(base_dir):
     55       filename = filename[len(base_dir)+1:]
     56     print >> sys.stderr, '  %s at %s:%d' % (function, filename, line)
     57     print >> sys.stderr, '    %s' % text
     58 
     59   # Format the exception.
     60   if exception_string:
     61     print >> sys.stderr, exception_string
     62 
     63   # Format the locals.
     64   local_variables = [(variable, value) for variable, value in
     65                      frame.f_locals.iteritems() if variable != 'self']
     66   print >> sys.stderr
     67   print >> sys.stderr, 'Locals:'
     68   if local_variables:
     69     longest_variable = max(len(v) for v, _ in local_variables)
     70     for variable, value in sorted(local_variables):
     71       value = repr(value)
     72       possibly_truncated_value = _AbbreviateMiddleOfString(value, ' ... ', 1024)
     73       truncation_indication = ''
     74       if len(possibly_truncated_value) != len(value):
     75         truncation_indication = ' (truncated)'
     76       print >> sys.stderr, '  %s: %s%s' % (variable.ljust(longest_variable + 1),
     77                                            possibly_truncated_value,
     78                                            truncation_indication)
     79   else:
     80     print >> sys.stderr, '  No locals!'
     81 
     82   print >> sys.stderr
     83   sys.stderr.flush()
     84 
     85 
     86 def _AbbreviateMiddleOfString(target, middle, max_length):
     87   if max_length < 0:
     88     raise ValueError('Must provide positive max_length')
     89   if len(middle) > max_length:
     90     raise ValueError('middle must not be greater than max_length')
     91 
     92   if len(target) <= max_length:
     93     return target
     94   half_length = (max_length - len(middle)) / 2.
     95   return (target[:int(math.floor(half_length))] + middle +
     96           target[-int(math.ceil(half_length)):])
     97