[Python-checkins] CVS: python/dist/src/Lib cgitb.py,1.1,1.1.2.1

Barry Warsaw bwarsaw@users.sourceforge.net
Tue, 21 Aug 2001 05:49:07 -0700


Update of /cvsroot/python/python/dist/src/Lib
In directory usw-pr-cvs1:/tmp/cvs-serv3386

Modified Files:
      Tag: r22a2-branch
	cgitb.py 
Log Message:
Merging in Ka-Ping's changes from the trunk:

Enhancements: 
- file URL now starts with "file://" (standard) rather than "file:" 
- new optional argument 'context' to enable() 
- repeated variable names don't have their values shown twice 
- dotted attributes are shown; missing attributes handled reasonably 
- highlight the whole logical line even if it has multiple physical lines 
- use nice generator interface to tokenize 
- formatting fixed so that it looks good in lynx, links, and w3m too 


Index: cgitb.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/cgitb.py,v
retrieving revision 1.1
retrieving revision 1.1.2.1
diff -C2 -d -r1.1 -r1.1.2.1
*** cgitb.py	2001/08/18 04:04:50	1.1
--- cgitb.py	2001/08/21 12:49:05	1.1.2.1
***************
*** 9,14 ****
      display     - if true, tracebacks are displayed in the web browser
      logdir      - if set, tracebacks are written to files in this directory
  
! By default, tracebacks are displayed but not written to files.
  
  Alternatively, if you have caught an exception and want cgitb to display it
--- 9,15 ----
      display     - if true, tracebacks are displayed in the web browser
      logdir      - if set, tracebacks are written to files in this directory
+     context     - number of lines of source code to show for each stack frame
  
! By default, tracebacks are displayed but not saved, and context is 5.
  
  Alternatively, if you have caught an exception and want cgitb to display it
***************
*** 24,36 ****
  Content-Type: text/html
  
! <body bgcolor="#f0f0ff"><font color="#f0f0ff" size="-5"> -->
! <body bgcolor="#f0f0ff"><font color="#f0f0ff" size="-5"> --> -->
  </font> </font> </font> </script> </object> </blockquote> </pre>
  </table> </table> </table> </table> </table> </font> </font> </font>'''
  
! def html(etype, evalue, etb, context=5):
!     """Return a nice HTML document describing the traceback."""
!     import sys, os, types, time, traceback
!     import keyword, tokenize, linecache, inspect, pydoc
  
      if type(etype) is types.ClassType:
--- 25,71 ----
  Content-Type: text/html
  
! <body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> -->
! <body bgcolor="#f0f0f8"><font color="#f0f0f8" size="-5"> --> -->
  </font> </font> </font> </script> </object> </blockquote> </pre>
  </table> </table> </table> </table> </table> </font> </font> </font>'''
  
! __UNDEF__ = []                          # a special sentinel object
! def small(text): return '<small>' + text + '</small>'
! def strong(text): return '<strong>' + text + '</strong>'
! def grey(text): return '<font color="#909090">' + text + '</font>'
! 
! def lookup(name, frame, locals):
!     """Find the value for a given name in the given environment."""
!     if name in locals:
!         return 'local', locals[name]
!     if name in frame.f_globals:
!         return 'global', frame.f_globals[name]
!     return None, __UNDEF__
! 
! def scanvars(reader, frame, locals):
!     """Scan one logical line of Python and look up values of variables used."""
!     import tokenize, keyword
!     vars, lasttoken, parent, prefix = [], None, None, ''
!     for ttype, token, start, end, line in tokenize.generate_tokens(reader):
!         if ttype == tokenize.NEWLINE: break
!         if ttype == tokenize.NAME and token not in keyword.kwlist:
!             if lasttoken == '.':
!                 if parent is not __UNDEF__:
!                     value = getattr(parent, token, __UNDEF__)
!                     vars.append((prefix + token, prefix, value))
!             else:
!                 where, value = lookup(token, frame, locals)
!                 vars.append((token, where, value))
!         elif token == '.':
!             prefix += lasttoken + '.'
!             parent = value
!         else:
!             parent, prefix = None, ''
!         lasttoken = token
!     return vars
! 
! def html((etype, evalue, etb), context=5):
!     """Return a nice HTML document describing a given traceback."""
!     import sys, os, types, time, traceback, linecache, inspect, pydoc
  
      if type(etype) is types.ClassType:
***************
*** 38,117 ****
      pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
      date = time.ctime(time.time())
!     head = '<body bgcolor="#f0f0ff">' + pydoc.html.heading(
          '<big><big><strong>%s</strong></big></big>' % str(etype),
!         '#ffffff', '#aa55cc', pyver + '<br>' + date) + '''
! <p>A problem occurred in a Python script.
! Here is the sequence of function calls leading up to
! the error, with the most recent (innermost) call last.'''
  
!     indent = '<tt><small>' + '&nbsp;' * 5 + '</small>&nbsp;</tt>'
      frames = []
      records = inspect.getinnerframes(etb, context)
      for frame, file, lnum, func, lines, index in records:
          file = file and os.path.abspath(file) or '?'
!         link = '<a href="file:%s">%s</a>' % (file, pydoc.html.escape(file))
          args, varargs, varkw, locals = inspect.getargvalues(frame)
!         if func == '?':
!             call = ''
!         else:
!             def eqrepr(value): return '=' + pydoc.html.repr(value)
!             call = 'in <strong>%s</strong>' % func + inspect.formatargvalues(
!                     args, varargs, varkw, locals, formatvalue=eqrepr)
! 
!         names = []
!         def tokeneater(type, token, start, end, line):
!             if type == tokenize.NAME and token not in keyword.kwlist:
!                 if token not in names: names.append(token)
!             if type == tokenize.NEWLINE: raise IndexError
!         def linereader(lnum=[lnum]):
!             line = linecache.getline(file, lnum[0])
!             lnum[0] += 1
!             return line
  
!         try:
!             tokenize.tokenize(linereader, tokeneater)
!         except IndexError: pass
!         lvals = []
!         for name in names:
!             if name in frame.f_code.co_varnames:
!                 if locals.has_key(name):
!                     value = pydoc.html.repr(locals[name])
!                 else:
!                     value = '<em>undefined</em>'
!                 name = '<strong>%s</strong>' % name
!             else:
!                 if frame.f_globals.has_key(name):
!                     value = pydoc.html.repr(frame.f_globals[name])
!                 else:
!                     value = '<em>undefined</em>'
!                 name = '<em>global</em> <strong>%s</strong>' % name
!             lvals.append('%s&nbsp;= %s' % (name, value))
!         if lvals:
!             lvals = indent + '''
! <small><font color="#909090">%s</font></small><br>''' % (', '.join(lvals))
!         else:
!             lvals = ''
  
!         level = '''
! <table width="100%%" bgcolor="#d8bbff" cellspacing=0 cellpadding=2 border=0>
! <tr><td>%s %s</td></tr></table>\n''' % (link, call)
!         excerpt = []
          if index is not None:
              i = lnum - index
              for line in lines:
!                 num = '<small><font color="#909090">%s</font></small>' % (
!                     '&nbsp;' * (5-len(str(i))) + str(i))
!                 line = '<tt>%s&nbsp;%s</tt>' % (num, pydoc.html.preformat(line))
!                 if i == lnum:
!                     line = '''
! <table width="100%%" bgcolor="#ffccee" cellspacing=0 cellpadding=0 border=0>
! <tr><td>%s</td></tr></table>\n''' % line
!                 excerpt.append('\n' + line)
!                 if i == lnum:
!                     excerpt.append(lvals)
!                 i = i + 1
!         frames.append('<p>' + level + '\n'.join(excerpt))
  
!     exception = ['<p><strong>%s</strong>: %s' % (str(etype), str(evalue))]
      if type(evalue) is types.InstanceType:
          for name in dir(evalue):
--- 73,133 ----
      pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
      date = time.ctime(time.time())
!     head = '<body bgcolor="#f0f0f8">' + pydoc.html.heading(
          '<big><big><strong>%s</strong></big></big>' % str(etype),
!         '#ffffff', '#6622aa', pyver + '<br>' + date) + '''
! <p>A problem occurred in a Python script.  Here is the sequence of
! function calls leading up to the error, in the order they occurred.'''
  
!     indent = '<tt>' + small('&nbsp;' * 5) + '&nbsp;</tt>'
      frames = []
      records = inspect.getinnerframes(etb, context)
      for frame, file, lnum, func, lines, index in records:
          file = file and os.path.abspath(file) or '?'
!         link = '<a href="file://%s">%s</a>' % (file, pydoc.html.escape(file))
          args, varargs, varkw, locals = inspect.getargvalues(frame)
!         call = ''
!         if func != '?':
!             call = 'in ' + strong(func) + \
!                 inspect.formatargvalues(args, varargs, varkw, locals,
!                     formatvalue=lambda value: '=' + pydoc.html.repr(value))
  
!         highlight = {}
!         def reader(lnum=[lnum]):
!             highlight[lnum[0]] = 1
!             try: return linecache.getline(file, lnum[0])
!             finally: lnum[0] += 1
!         vars = scanvars(reader, frame, locals)
  
!         rows = ['<tr><td bgcolor="#d8bbff">%s%s %s</td></tr>' %
!                 ('<big>&nbsp;</big>', link, call)]
          if index is not None:
              i = lnum - index
              for line in lines:
!                 num = small('&nbsp;' * (5-len(str(i))) + str(i)) + '&nbsp;'
!                 line = '<tt>%s%s</tt>' % (num, pydoc.html.preformat(line))
!                 if i in highlight:
!                     rows.append('<tr><td bgcolor="#ffccee">%s</td></tr>' % line)
!                 else:
!                     rows.append('<tr><td>%s</td></tr>' % grey(line))
!                 i += 1
  
!         done, dump = {}, []
!         for name, where, value in vars:
!             if name in done: continue
!             done[name] = 1
!             if value is not __UNDEF__:
!                 if where == 'global': name = '<em>global</em> ' + strong(name)
!                 elif where == 'local': name = strong(name)
!                 else: name = where + strong(name.split('.')[-1])
!                 dump.append('%s&nbsp;= %s' % (name, pydoc.html.repr(value)))
!             else:
!                 dump.append(name + ' <em>undefined</em>')
! 
!         rows.append('<tr><td>%s</td></tr>' % small(grey(', '.join(dump))))
!         frames.append('''<p>
! <table width="100%%" cellspacing=0 cellpadding=0 border=0>
! %s</table>''' % '\n'.join(rows))
! 
!     exception = ['<p>%s: %s' % (strong(str(etype)), str(evalue))]
      if type(evalue) is types.InstanceType:
          for name in dir(evalue):
***************
*** 120,163 ****
  
      import traceback
-     plaintrace = ''.join(traceback.format_exception(etype, evalue, etb))
- 
      return head + ''.join(frames) + ''.join(exception) + '''
  
  
! <!-- The above is a description of an error that occurred in a Python program.
!      It is formatted for display in a Web browser because it appears that
!      we are running in a CGI environment.  In case you are viewing this
!      message outside of a Web browser, here is the original error traceback:
  
  %s
  -->
! ''' % plaintrace
  
  class Hook:
!     def __init__(self, display=1, logdir=None):
          self.display = display          # send tracebacks to browser if true
          self.logdir = logdir            # log tracebacks to files if not None
  
      def __call__(self, etype, evalue, etb):
-         """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
          self.handle((etype, evalue, etb))
  
      def handle(self, info=None):
!         import sys, os
          info = info or sys.exc_info()
-         text = 0
          print reset()
  
          try:
!             doc = html(*info)
          except:                         # just in case something goes wrong
              import traceback
!             doc = ''.join(traceback.format_exception(*info))
!             text = 1
  
          if self.display:
              if text:
                  doc = doc.replace('&', '&amp;').replace('<', '&lt;')
!                 print '<pre>', doc, '</pre>'
              else:
                  print doc
--- 136,176 ----
  
      import traceback
      return head + ''.join(frames) + ''.join(exception) + '''
  
  
! <!-- The above is a description of an error in a Python program, formatted
!      for a Web browser because the 'cgitb' module was enabled.  In case you
!      are not reading this in a Web browser, here is the original traceback:
  
  %s
  -->
! ''' % ''.join(traceback.format_exception(etype, evalue, etb))
  
  class Hook:
!     """A hook to replace sys.excepthook that shows tracebacks in HTML."""
! 
!     def __init__(self, display=1, logdir=None, context=5):
          self.display = display          # send tracebacks to browser if true
          self.logdir = logdir            # log tracebacks to files if not None
+         self.context = context          # number of source code lines per frame
  
      def __call__(self, etype, evalue, etb):
          self.handle((etype, evalue, etb))
  
      def handle(self, info=None):
!         import sys
          info = info or sys.exc_info()
          print reset()
  
          try:
!             text, doc = 0, html(info, self.context)
          except:                         # just in case something goes wrong
              import traceback
!             text, doc = 1, ''.join(traceback.format_exception(*info))
  
          if self.display:
              if text:
                  doc = doc.replace('&', '&amp;').replace('<', '&lt;')
!                 print '<pre>' + doc + '</pre>'
              else:
                  print doc
***************
*** 166,170 ****
  
          if self.logdir is not None:
!             import tempfile
              name = tempfile.mktemp(['.html', '.txt'][text])
              path = os.path.join(self.logdir, os.path.basename(name))
--- 179,183 ----
  
          if self.logdir is not None:
!             import os, tempfile
              name = tempfile.mktemp(['.html', '.txt'][text])
              path = os.path.join(self.logdir, os.path.basename(name))
***************
*** 173,182 ****
                  file.write(doc)
                  file.close()
!                 print '<p>%s contains the description of this error.' % path
              except:
!                 print '<p>Tried to write to %s, but failed.' % path
  
  handler = Hook().handle
! def enable(display=1, logdir=None):
      import sys
!     sys.excepthook = Hook(display, logdir)
--- 186,200 ----
                  file.write(doc)
                  file.close()
!                 print '<p> %s contains the description of this error.' % path
              except:
!                 print '<p> Tried to save traceback to %s, but failed.' % path
  
  handler = Hook().handle
! def enable(display=1, logdir=None, context=5):
!     """Install an exception handler that formats tracebacks as HTML.
! 
!     The optional argument 'display' can be set to 0 to suppress sending the
!     traceback to the browser, and 'logdir' can be set to a directory to cause
!     tracebacks to be written to files there."""
      import sys
!     sys.excepthook = Hook(display, logdir, context)