Home | History | Annotate | Download | only in paste
      1 # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
      2 # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
      3 
      4 """
      5 WSGI middleware
      6 
      7 Captures any exceptions and prints a pretty report.  See the `cgitb
      8 documentation <http://python.org/doc/current/lib/module-cgitb.html>`_
      9 for more.
     10 """
     11 
     12 import cgitb
     13 import six
     14 from six.moves import cStringIO as StringIO
     15 import sys
     16 
     17 from paste.util import converters
     18 
     19 class NoDefault(object):
     20     pass
     21 
     22 class CgitbMiddleware(object):
     23 
     24     def __init__(self, app,
     25                  global_conf=None,
     26                  display=NoDefault,
     27                  logdir=None,
     28                  context=5,
     29                  format="html"):
     30         self.app = app
     31         if global_conf is None:
     32             global_conf = {}
     33         if display is NoDefault:
     34             display = global_conf.get('debug')
     35         if isinstance(display, six.string_types):
     36             display = converters.asbool(display)
     37         self.display = display
     38         self.logdir = logdir
     39         self.context = int(context)
     40         self.format = format
     41 
     42     def __call__(self, environ, start_response):
     43         try:
     44             app_iter = self.app(environ, start_response)
     45             return self.catching_iter(app_iter, environ)
     46         except:
     47             exc_info = sys.exc_info()
     48             start_response('500 Internal Server Error',
     49                            [('content-type', 'text/html')],
     50                            exc_info)
     51             response = self.exception_handler(exc_info, environ)
     52             if six.PY3:
     53                 response = response.encode('utf8')
     54             return [response]
     55 
     56     def catching_iter(self, app_iter, environ):
     57         if not app_iter:
     58             raise StopIteration
     59         error_on_close = False
     60         try:
     61             for v in app_iter:
     62                 yield v
     63             if hasattr(app_iter, 'close'):
     64                 error_on_close = True
     65                 app_iter.close()
     66         except:
     67             response = self.exception_handler(sys.exc_info(), environ)
     68             if not error_on_close and hasattr(app_iter, 'close'):
     69                 try:
     70                     app_iter.close()
     71                 except:
     72                     close_response = self.exception_handler(
     73                         sys.exc_info(), environ)
     74                     response += (
     75                         '<hr noshade>Error in .close():<br>%s'
     76                         % close_response)
     77             if six.PY3:
     78                 response = response.encode('utf8')
     79             yield response
     80 
     81     def exception_handler(self, exc_info, environ):
     82         dummy_file = StringIO()
     83         hook = cgitb.Hook(file=dummy_file,
     84                           display=self.display,
     85                           logdir=self.logdir,
     86                           context=self.context,
     87                           format=self.format)
     88         hook(*exc_info)
     89         return dummy_file.getvalue()
     90 
     91 def make_cgitb_middleware(app, global_conf,
     92                           display=NoDefault,
     93                           logdir=None,
     94                           context=5,
     95                           format='html'):
     96     """
     97     Wraps the application in the ``cgitb`` (standard library)
     98     error catcher.
     99 
    100       display:
    101         If true (or debug is set in the global configuration)
    102         then the traceback will be displayed in the browser
    103 
    104       logdir:
    105         Writes logs of all errors in that directory
    106 
    107       context:
    108         Number of lines of context to show around each line of
    109         source code
    110     """
    111     from paste.deploy.converters import asbool
    112     if display is not NoDefault:
    113         display = asbool(display)
    114     if 'debug' in global_conf:
    115         global_conf['debug'] = asbool(global_conf['debug'])
    116     return CgitbMiddleware(
    117         app, global_conf=global_conf,
    118         display=display,
    119         logdir=logdir,
    120         context=context,
    121         format=format)
    122