pylons.middleware
Covered: 156 lines
Missed: 0 lines
Skipped 40 lines
Percent: 100 %
  1
"""Pylons' WSGI middlewares"""
  2
import logging
  3
import os.path
  5
from paste.deploy.converters import asbool
  6
from paste.urlparser import StaticURLParser
  7
from weberror.evalexception import EvalException
  8
from weberror.errormiddleware import ErrorMiddleware
  9
from webob import Request, Response
 10
from webhelpers.html import literal
 12
import pylons
 13
from pylons.error import template_error_formatters
 14
from pylons.util import call_wsgi_application
 16
__all__ = ['ErrorHandler', 'StaticJavascripts', 'error_document_template',
 17
           'footer_html', 'head_html', 'media_path']
 19
log = logging.getLogger(__name__)
 21
media_path = os.path.join(os.path.dirname(__file__), 'media')
 23
head_html = """\
 24
<link rel="stylesheet" href="{{prefix}}/media/pylons/style/itraceback.css" \
 25
type="text/css" media="screen" />"""
 27
footer_html = """\
 28
<script src="{{prefix}}/media/pylons/javascripts/traceback.js"></script>
 29
<script>
 30
var TRACEBACK = {
 31
    uri: "{{prefix}}",
 32
    host: "%s",
 33
    traceback: "/tracebacks"
 34
}
 35
</script>
 36
<div id="service_widget">
 37
<h2 class="assistance">Online Assistance</h2>
 38
<div id="nv">
 39
<ul id="supportnav">
 40
    <li class="nav active"><a class="overview" href="#">Overview</a></li>
 41
    <li class="nav"><a class="search" href="#">Search Mail Lists</a></li>
 42
    <li class="nav"><a class="posttraceback" href="#">Post Traceback</a></li>
 43
</ul>
 44
</div>
 45
<div class="clearfix">&nbsp;</div>
 46
<div class="overviewtab">
 47
<b>Looking for help?</b>
 49
<p>Here are a few tips for troubleshooting if the above traceback isn't
 50
helping out.</p>
 52
<ol>
 53
<li>Search the mail list</li>
 54
<li>Post the traceback, and ask for help on IRC</li>
 55
<li>Post a message to the mail list, referring to the posted traceback</li>
 57
</div>
 58
<div class="posttracebacktab">
 59
<p><b>Note:</b> Clicking this button will post your traceback to the PylonsHQ website.
 60
The traceback includes the module names, Python version, and lines of code that you
 61
can see above. All tracebacks are posted anonymously unless you're logged into the
 62
PylonsHQ website in this browser.</p>
 63
<input type="button" href="#" class="submit_traceback" value="Send TraceBack to PylonsHQ" style="text-align: center;"/>
 64
</div>
 66
<div class="searchtab">
 67
<p>The following mail lists will be searched:<br />
 68
<input type="checkbox" name="lists" value="pylons" checked="checked" /> Pylons<br />
 69
<input type="checkbox" name="lists" value="python" /> Python<br />
 70
<input type="checkbox" name="lists" value="mako" /> Mako<br />
 71
<input type="checkbox" name="lists" value="sqlalchemy" /> SQLAlchemy</p>
 72
<p class="query">for: <input type="text" name="query" class="query" /></p>
 74
<p><input type="submit" value="Search" /></p>
 75
<div class="searchresults">
 77
</div>
 78
</div>
 80
</div>
 81
<div id="pylons_logo">\
 82
<img src="{{prefix}}/media/pylons/img/pylons-powered-02.png" /></div>
 83
<div class="credits">Pylons version %s</div>"""
 85
report_libs = ['pylons', 'genshi', 'sqlalchemy']
 87
def ErrorHandler(app, global_conf, **errorware):
 88
    """ErrorHandler Toggle
 90
    If debug is enabled, this function will return the app wrapped in
 91
    the WebError ``EvalException`` middleware which displays
 92
    interactive debugging sessions when a traceback occurs.
 94
    Otherwise, the app will be wrapped in the WebError
 95
    ``ErrorMiddleware``, and the ``errorware`` dict will be passed into
 96
    it. The ``ErrorMiddleware`` handles sending an email to the address
 97
    listed in the .ini file, under ``email_to``.
 99
    """
100
    if asbool(global_conf.get('debug')):
101
        footer = footer_html % (pylons.config.get('traceback_host', 
102
                                                  'pylonshq.com'),
103
                                pylons.__version__)
104
        py_media = dict(pylons=media_path)
105
        app = EvalException(app, global_conf, 
106
                            templating_formatters=template_error_formatters,
107
                            media_paths=py_media, head_html=head_html, 
108
                            footer_html=footer,
109
                            libraries=report_libs)
110
    else:
111
        app = ErrorMiddleware(app, global_conf, **errorware)
112
    return app
115
class StatusCodeRedirect(object):
116
    """Internally redirects a request based on status code
118
    StatusCodeRedirect watches the response of the app it wraps. If the 
119
    response is an error code in the errors sequence passed the request
120
    will be re-run with the path URL set to the path passed in.
122
    This operation is non-recursive and the output of the second 
123
    request will be used no matter what it is.
125
    Should an application wish to bypass the error response (ie, to 
126
    purposely return a 401), set 
127
    ``environ['pylons.status_code_redirect'] = True`` in the application.
129
    """
130
    def __init__(self, app, errors=(400, 401, 403, 404),
131
                 path='/error/document'):
132
        """Initialize the ErrorRedirect
134
        ``errors``
135
            A sequence (list, tuple) of error code integers that should
136
            be caught.
137
        ``path``
138
            The path to set for the next request down to the 
139
            application. 
141
        """
142
        self.app = app
143
        self.error_path = path
146
        self.errors = tuple([str(x) for x in errors])
148
    def __call__(self, environ, start_response):
149
        status, headers, app_iter, exc_info = call_wsgi_application(
150
            self.app, environ, catch_exc_info=True)
151
        if status[:3] in self.errors and \
152
            'pylons.status_code_redirect' not in environ and self.error_path:
154
            environ['pylons.original_response'] = Response(
155
                status=status, headerlist=headers, app_iter=app_iter)
156
            environ['pylons.original_request'] = Request(environ)
159
            new_environ = environ.copy()
160
            new_environ['PATH_INFO'] = self.error_path
162
            newstatus, headers, app_iter, exc_info = call_wsgi_application(
163
                    self.app, new_environ, catch_exc_info=True)
164
        start_response(status, headers, exc_info)
165
        return app_iter
168
error_document_template = literal("""\
169
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
170
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
171
<head>
172
 <title>Server Error %(code)s</title>
173
<!-- CSS Imports -->
174
<link rel="stylesheet" href="%(prefix)s/error/style/black.css" type="text/css" media="screen" />
176
<!-- Favorite Icons -->
177
<link rel="icon" href="%(prefix)s/error/img/favicon.ico" type="image/png" />
179
<style type="text/css">
180
        .red {
181
            color:#FF0000;
182
        }
183
        .bold {
184
            font-weight: bold;
185
        }
186
</style>
187
</head>
189
<body>
190
    <div id="container">
191
        %(message)s
192
    </div>
193
</body>
194
</html>
195
""")