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""" 5WSGI middleware 6 7Captures any exceptions and prints a pretty report. See the `cgitb 8documentation <http://python.org/doc/current/lib/module-cgitb.html>`_ 9for more. 10""" 11 12import cgitb 13import six 14from six.moves import cStringIO as StringIO 15import sys 16 17from paste.util import converters 18 19class NoDefault(object): 20 pass 21 22class 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 91def 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