• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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