diff options
Diffstat (limited to 'pecan/middleware/debug.py')
-rw-r--r-- | pecan/middleware/debug.py | 420 |
1 files changed, 96 insertions, 324 deletions
diff --git a/pecan/middleware/debug.py b/pecan/middleware/debug.py index ec2faa0..1a6a482 100644 --- a/pecan/middleware/debug.py +++ b/pecan/middleware/debug.py @@ -1,324 +1,96 @@ -from traceback import print_exc -from pprint import pformat -import pdb - -from six.moves import cStringIO as StringIO - -from mako.template import Template -from webob import Response - -from .resources import (pecan_image, xregexp_js, syntax_js, syntax_css, theme, - brush) - - -debug_template_raw = '''<html> - <head> - <title>Pecan - Application Error</title> - - <link rel="stylesheet" type="text/css" href="${syntax_css}" /> - <link rel="stylesheet" type="text/css" href="${theme}" /> - - <script type="text/javascript" src="${xregexp_js}"></script> - <script type="text/javascript" src="${syntax_js}"> - /** - * SyntaxHighlighter - * http://alexgorbatchev.com/SyntaxHighlighter - * - * SyntaxHighlighter is donationware. If you are using it, please donate. - * http://alexgorbatchev.com/SyntaxHighlighter/donate.html - * - * @version - * 3.0.83 (July 02 2010) - * - * @copyright - * Copyright (C) 2004-2010 Alex Gorbatchev. - * - * @license - * Dual licensed under the MIT and GPL licenses. - */ - </script> - <script type="text/javascript" src="${brush}"> - /** - * SyntaxHighlighter - * http://alexgorbatchev.com/SyntaxHighlighter - * - * SyntaxHighlighter is donationware. If you are using it, please donate. - * http://alexgorbatchev.com/SyntaxHighlighter/donate.html - * - * @version - * 3.0.83 (July 02 2010) - * - * @copyright - * Copyright (C) 2004-2010 Alex Gorbatchev. - * - * @license - * Dual licensed under the MIT and GPL licenses. - */ - </script> - - <style type="text/css"> - body { - color: #000; - background: #FFF; - font-family: 'Helvetica Neue', 'Helvetica', 'Verdana', sans-serif; - font-size: 12px; - padding: 0; - margin: 0; - } - - a { - color: #FAFF78; - } - - h1, h2, h3, h4, h5, h6 { - font-family: 'Helvetica', sans-serif; - } - - h1 { - margin: 0; - padding: .75em 1.5em 1em 1.5em; - color: #F90; - font-size: 14px; - font-weight: bold; - } - - h1 img { - padding-right: 5px; - } - - h2 { - color: #311F00; - } - - header { - width: 100%; - background: #311F00; - } - - div#error-content { - padding: 0 2em; - } - - .syntaxhighlighter a, - .syntaxhighlighter div, - .syntaxhighlighter code, - .syntaxhighlighter table, - .syntaxhighlighter table td, - .syntaxhighlighter table tr, - .syntaxhighlighter table tbody, - .syntaxhighlighter table thead, - .syntaxhighlighter table caption, - .syntaxhighlighter textarea { - font-family: monospace !important; - } - - .syntaxhighlighter .container { - background: #FDF6E3 !important; - padding: 1em !important; - } - - .syntaxhighlighter .container .line { - background: #FDF6E3 !important; - } - - .syntaxhighlighter .container .line .python.string { - color: #C70 !important; - } - - #debug { - background: #FDF6E3; - padding: 10px !important; - margin-top: 10px; - font-family: monospace; - } - - </style> - <script type="text/javascript"> - SyntaxHighlighter.defaults['gutter'] = false; - SyntaxHighlighter.defaults['toolbar'] = false; - SyntaxHighlighter.all() - </script> - - <script type="text/javascript"> - function get_request() { - /* ajax sans jquery makes me sad */ - var request = false; - - // Mozilla/Safari - if (window.XMLHttpRequest) { - request = new XMLHttpRequest(); - } - - // IE - else if (window.ActiveXObject) { - request = new ActiveXObject("Microsoft.XMLHTTP"); - } - - return request; - } - - function debug_request(btn) { - btn.disabled = true; - - request = get_request(); - request.open('GET', '/__pecan_initiate_pdb__', true); - request.onreadystatechange = function() { - if (request.readyState == 4) { - btn.disabled = false; - } - } - request.send(''); - - /* automatically timeout after 5 minutes, re-enabling the button */ - setTimeout(function() { - request.abort(); - }, 5 * 60 * 1000); - } - </script> - </head> - <body> - - <header> - <h1> - <img style="padding-top: 7px" - align="center" alt="pecan logo" - height="25" - src="${pecan_image}" /> - application error - </h1> - </header> - - <div id="error-content"> - - <p> - <b>To disable this interface, set </b> - <pre class="brush: python">conf.app.debug = False</pre> - </p> - - <h2>Traceback</h2> - <div id="traceback"> - <pre class="brush: python">${traceback}</pre> - </div> - - % if not debugging: - <b>Want to debug this request?</b> - <div id="debug"> - You can <button onclick="debug_request(this)"> - repeat this request - </button> with a Python debugger breakpoint. - </div> - % endif - - <h2>WSGI Environment</h2> - <div id="environ"> - <pre class="brush: python">${environment}</pre> - </div> - </div> - </body> -</html> -''' - -debug_template = Template(debug_template_raw) -__debug_environ__ = None - - -class PdbMiddleware(object): - def __init__(self, app, debugger): - self.app = app - self.debugger = debugger - - def __call__(self, environ, start_response): - try: - return self.app(environ, start_response) - except: - self.debugger() - - -class DebugMiddleware(object): - """A WSGI middleware that provides debugging assistance for development - environments. - - To enable the debugging middleware, simply set the ``debug`` flag to - ``True`` in your configuration file:: - - app = { - ... - 'debug': True, - ... - } - - Once enabled, the middleware will automatically catch exceptions raised by - your application, and display the Python stack trace and WSGI environment - in your browser for easy debugging. - - To further aid in debugging, the middleware includes the ability to repeat - the offending request, automatically inserting a breakpoint, and dropping - your console into the Python debugger, ``pdb.post_mortem``. - - You can also use any debugger with a suitable ``post_mortem`` entry point - such as the `PuDB Debugger <http://pypi.python.org/pypi/pudb>`_, - - For more information, refer to the `documentation for pdb - <http://docs.python.org/library/pdb.html>`_ available on the Python - website. - - :param app: the application to wrap. - :param debugger: a callable to start debugging, defaulting to the Python - debugger entry point ``pdb.post_mortem``. - """ - - def __init__(self, app, debugger=pdb.post_mortem): - self.app = app - self.debugger = debugger - - def __call__(self, environ, start_response): - if environ['wsgi.multiprocess']: - raise RuntimeError( - "The DebugMiddleware middleware is not usable in a " - "multi-process environment" - ) - - if environ.get('paste.testing'): - return self.app(environ, start_response) - - # initiate a PDB session if requested - global __debug_environ__ - debugging = environ['PATH_INFO'] == '/__pecan_initiate_pdb__' - if debugging: - PdbMiddleware(self.app, self.debugger)( - __debug_environ__, start_response - ) - environ = __debug_environ__ - - try: - return self.app(environ, start_response) - except: - # save the environ for debugging - if not debugging: - __debug_environ__ = environ - - # get a formatted exception - out = StringIO() - print_exc(file=out) - - # get formatted WSGI environment - formatted_environ = pformat(environ) - - # render our template - result = debug_template.render( - traceback=out.getvalue(), - environment=formatted_environ, - pecan_image=pecan_image, - xregexp_js=xregexp_js, - syntax_js=syntax_js, - brush=brush, - syntax_css=syntax_css, - theme=theme, - debugging=debugging - ) - - # construct and return our response - response = Response() - response.status_int = 400 - response.unicode_body = result - return response(environ, start_response) +__CONFIG_HELP__ = ''' +<div class="traceback"> + <b>To disable this interface, set </b> + <a target="window" + href="https://pecan.readthedocs.org/en/latest/deployment.html#disabling-debug-mode"> + <pre>conf.app.debug = False</pre> + </a> +</div> +''' # noqa + +try: + import re + from backlash.debug import DebuggedApplication + + class DebugMiddleware(DebuggedApplication): + + body_re = re.compile('(<body[^>]*>)', re.I) + + def debug_application(self, environ, start_response): + for part in super(DebugMiddleware, self).debug_application( + environ, start_response + ): + yield self.body_re.sub('\g<1>%s' % __CONFIG_HELP__, part) + + +except ImportError: + from traceback import print_exc + from pprint import pformat + + from mako.template import Template + from six.moves import cStringIO as StringIO + from webob import Response + from webob.exc import HTTPException + + debug_template_raw = '''<html> + <head> + <title>Pecan - Application Error</title> + <body> + <header> + <h1> + An error occurred! + </h1> + </header> + <div id="error-content"> + <p> + %(config_help)s + Pecan offers support for interactive debugging by installing the <a href="https://pypi.python.org/pypi/backlash" target="window">backlash</a> package: + <br /> + <b><pre>pip install backlash</pre></b> + ...and reloading this page. + </p> + <h2>Traceback</h2> + <div id="traceback"> + <pre>${traceback}</pre> + </div> + <h2>WSGI Environment</h2> + <div id="environ"> + <pre>${environment}</pre> + </div> + </div> + </body> + </html> + ''' % {'config_help': __CONFIG_HELP__} # noqa + + debug_template = Template(debug_template_raw) + + class DebugMiddleware(object): + + def __init__(self, app, *args, **kwargs): + self.app = app + + def __call__(self, environ, start_response): + try: + return self.app(environ, start_response) + except Exception as exc: + # get a formatted exception + out = StringIO() + print_exc(file=out) + + # get formatted WSGI environment + formatted_environ = pformat(environ) + + # render our template + result = debug_template.render( + traceback=out.getvalue(), + environment=formatted_environ + ) + + # construct and return our response + response = Response() + if isinstance(exc, HTTPException): + response.status_int = exc.status + else: + response.status_int = 500 + response.unicode_body = result + return response(environ, start_response) |