1b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) 2b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 3b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik""" 4b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikException-catching middleware that allows interactive debugging. 5b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 6b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikThis middleware catches all unexpected exceptions. A normal 7b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craiktraceback, like produced by 8b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik``paste.exceptions.errormiddleware.ErrorMiddleware`` is given, plus 9b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikcontrols to see local variables and evaluate expressions in a local 10b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikcontext. 11b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 12b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikThis can only be used in single-process environments, because 13b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craiksubsequent requests must go back to the same process that the 14b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikexception originally occurred in. Threaded or non-concurrent 15b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikenvironments both work. 16b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 17b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikThis shouldn't be used in production in any way. That would just be 18b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craiksilly. 19b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 20b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikIf calling from an XMLHttpRequest call, if the GET variable ``_`` is 21b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikgiven then it will make the response more compact (and less 22b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris CraikJavascripty), since if you use innerHTML it'll kill your browser. You 23b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikcan look for the header X-Debug-URL in your 500 responses if you want 24b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikto see the full debuggable traceback. Also, this URL is printed to 25b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik``wsgi.errors``, so you can open it up in another browser window. 26b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik""" 27b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 28b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom __future__ import print_function 29b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 30b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport sys 31b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport os 32b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport cgi 33b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport traceback 34b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport six 35b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom six.moves import cStringIO as StringIO 36b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport pprint 37b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport itertools 38b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport time 39b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikimport re 40b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom paste.exceptions import errormiddleware, formatter, collector 41b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom paste import wsgilib 42b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom paste import urlparser 43b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom paste import httpexceptions 44b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom paste import registry 45b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom paste import request 46b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom paste import response 47b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikfrom paste.evalexception import evalcontext 48b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 49b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craiklimit = 200 50b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 51b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef html_quote(v): 52b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 53b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Escape HTML characters, plus translate None to '' 54b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 55b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if v is None: 56b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '' 57b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return cgi.escape(str(v), 1) 58b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 59b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef preserve_whitespace(v, quote=True): 60b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 61b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Quote a value for HTML, preserving whitespace (translating 62b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik newlines to ``<br>`` and multiple spaces to use `` ``). 63b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 64b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik If ``quote`` is true, then the value will be HTML quoted first. 65b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 66b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if quote: 67b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik v = html_quote(v) 68b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik v = v.replace('\n', '<br>\n') 69b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik v = re.sub(r'()( +)', _repl_nbsp, v) 70b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik v = re.sub(r'(\n)( +)', _repl_nbsp, v) 71b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik v = re.sub(r'^()( +)', _repl_nbsp, v) 72b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '<code>%s</code>' % v 73b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 74b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef _repl_nbsp(match): 75b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(match.group(2)) == 1: 76b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return ' ' 77b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return match.group(1) + ' ' * (len(match.group(2))-1) + ' ' 78b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 79b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef simplecatcher(application): 80b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 81b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A simple middleware that catches errors and turns them into simple 82b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik tracebacks. 83b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 84b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def simplecatcher_app(environ, start_response): 85b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 86b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return application(environ, start_response) 87b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except: 88b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out = StringIO() 89b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik traceback.print_exc(file=out) 90b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik start_response('500 Server Error', 91b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik [('content-type', 'text/html')], 92b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik sys.exc_info()) 93b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik res = out.getvalue() 94b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return ['<h3>Error</h3><pre>%s</pre>' 95b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % html_quote(res)] 96b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return simplecatcher_app 97b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 98b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef wsgiapp(): 99b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 100b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Turns a function or method into a WSGI application. 101b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 102b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def decorator(func): 103b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def wsgiapp_wrapper(*args): 104b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # we get 3 args when this is a method, two when it is 105b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # a function :( 106b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(args) == 3: 107b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ = args[1] 108b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik start_response = args[2] 109b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args = [args[0]] 110b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 111b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ, start_response = args 112b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik args = [] 113b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def application(environ, start_response): 114b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik form = wsgilib.parse_formvars(environ, 115b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik include_get_vars=True) 116b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik headers = response.HeaderDict( 117b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik {'content-type': 'text/html', 118b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'status': '200 OK'}) 119b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik form['environ'] = environ 120b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik form['headers'] = headers 121b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik res = func(*args, **form.mixed()) 122b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik status = headers.pop('status') 123b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik start_response(status, headers.headeritems()) 124b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return [res] 125b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik app = httpexceptions.make_middleware(application) 126b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik app = simplecatcher(app) 127b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return app(environ, start_response) 128b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik wsgiapp_wrapper.exposed = True 129b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return wsgiapp_wrapper 130b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return decorator 131b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 132b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef get_debug_info(func): 133b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 134b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik A decorator (meant to be used under ``wsgiapp()``) that resolves 135b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik the ``debugcount`` variable to a ``DebugInfo`` object (or gives an 136b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik error if it can't be found). 137b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 138b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def debug_info_replacement(self, **form): 139b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 140b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if 'debugcount' not in form: 141b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise ValueError('You must provide a debugcount parameter') 142b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik debugcount = form.pop('debugcount') 143b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 144b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik debugcount = int(debugcount) 145b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except ValueError: 146b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise ValueError('Bad value for debugcount') 147b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if debugcount not in self.debug_infos: 148b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise ValueError( 149b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'Debug %s no longer found (maybe it has expired?)' 150b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % debugcount) 151b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik debug_info = self.debug_infos[debugcount] 152b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return func(self, debug_info=debug_info, **form) 153b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except ValueError as e: 154b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik form['headers']['status'] = '500 Server Error' 155b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '<html>There was an error: %s</html>' % html_quote(e) 156b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return debug_info_replacement 157b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 158b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdebug_counter = itertools.count(int(time.time())) 159b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef get_debug_count(environ): 160b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 161b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Return the unique debug count for the current request 162b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 163b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if 'paste.evalexception.debug_count' in environ: 164b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return environ['paste.evalexception.debug_count'] 165b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 166b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ['paste.evalexception.debug_count'] = next = six.next(debug_counter) 167b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return next 168b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 169b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass EvalException(object): 170b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 171b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, application, global_conf=None, 172b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik xmlhttp_key=None): 173b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.application = application 174b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.debug_infos = {} 175b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if xmlhttp_key is None: 176b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if global_conf is None: 177b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik xmlhttp_key = '_' 178b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 179b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik xmlhttp_key = global_conf.get('xmlhttp_key', '_') 180b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.xmlhttp_key = xmlhttp_key 181b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 182b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __call__(self, environ, start_response): 183b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert not environ['wsgi.multiprocess'], ( 184b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik "The EvalException middleware is not usable in a " 185b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik "multi-process environment") 186b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ['paste.evalexception'] = self 187b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if environ.get('PATH_INFO', '').startswith('/_debug/'): 188b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self.debug(environ, start_response) 189b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 190b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self.respond(environ, start_response) 191b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 192b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def debug(self, environ, start_response): 193b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert request.path_info_pop(environ) == '_debug' 194b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik next_part = request.path_info_pop(environ) 195b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik method = getattr(self, next_part, None) 196b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not method: 197b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exc = httpexceptions.HTTPNotFound( 198b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '%r not found when parsing %r' 199b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % (next_part, wsgilib.construct_url(environ))) 200b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return exc.wsgi_application(environ, start_response) 201b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not getattr(method, 'exposed', False): 202b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exc = httpexceptions.HTTPForbidden( 203b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '%r not allowed' % next_part) 204b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return exc.wsgi_application(environ, start_response) 205b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return method(environ, start_response) 206b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 207b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def media(self, environ, start_response): 208b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 209b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Static path where images and other files live 210b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 211b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik app = urlparser.StaticURLParser( 212b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik os.path.join(os.path.dirname(__file__), 'media')) 213b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return app(environ, start_response) 214b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik media.exposed = True 215b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 216b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def mochikit(self, environ, start_response): 217b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 218b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Static path where MochiKit lives 219b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 220b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik app = urlparser.StaticURLParser( 221b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik os.path.join(os.path.dirname(__file__), 'mochikit')) 222b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return app(environ, start_response) 223b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik mochikit.exposed = True 224b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 225b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def summary(self, environ, start_response): 226b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 227b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Returns a JSON-format summary of all the cached 228b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exception reports 229b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 230b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik start_response('200 OK', [('Content-type', 'text/x-json')]) 231b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = []; 232b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik items = self.debug_infos.values() 233b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik items.sort(lambda a, b: cmp(a.created, b.created)) 234b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik data = [item.json() for item in items] 235b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return [repr(data)] 236b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik summary.exposed = True 237b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 238b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def view(self, environ, start_response): 239b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 240b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik View old exception reports 241b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 242b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik id = int(request.path_info_pop(environ)) 243b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if id not in self.debug_infos: 244b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik start_response( 245b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '500 Server Error', 246b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik [('Content-type', 'text/html')]) 247b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return [ 248b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik "Traceback by id %s does not exist (maybe " 249b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik "the server has been restarted?)" 250b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % id] 251b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik debug_info = self.debug_infos[id] 252b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return debug_info.wsgi_application(environ, start_response) 253b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik view.exposed = True 254b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 255b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def make_view_url(self, environ, base_path, count): 256b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return base_path + '/_debug/view/%s' % count 257b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 258b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #@wsgiapp() 259b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #@get_debug_info 260b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def show_frame(self, tbid, debug_info, **kw): 261b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik frame = debug_info.frame(int(tbid)) 262b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik vars = frame.tb_frame.f_locals 263b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if vars: 264b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik registry.restorer.restoration_begin(debug_info.counter) 265b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik local_vars = make_table(vars) 266b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik registry.restorer.restoration_end() 267b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 268b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik local_vars = 'No local vars' 269b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return input_form(tbid, debug_info) + local_vars 270b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 271b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik show_frame = wsgiapp()(get_debug_info(show_frame)) 272b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 273b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #@wsgiapp() 274b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik #@get_debug_info 275b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def exec_input(self, tbid, debug_info, input, **kw): 276b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not input.strip(): 277b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '' 278b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik input = input.rstrip() + '\n' 279b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik frame = debug_info.frame(int(tbid)) 280b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik vars = frame.tb_frame.f_locals 281b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik glob_vars = frame.tb_frame.f_globals 282b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik context = evalcontext.EvalContext(vars, glob_vars) 283b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik registry.restorer.restoration_begin(debug_info.counter) 284b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik output = context.exec_expr(input) 285b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik registry.restorer.restoration_end() 286b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik input_html = formatter.str2html(input) 287b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return ('<code style="color: #060">>>></code> ' 288b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '<code>%s</code><br>\n%s' 289b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % (preserve_whitespace(input_html, quote=False), 290b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik preserve_whitespace(output))) 291b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 292b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exec_input = wsgiapp()(get_debug_info(exec_input)) 293b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 294b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def respond(self, environ, start_response): 295b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if environ.get('paste.throw_errors'): 296b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self.application(environ, start_response) 297b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik base_path = request.construct_url(environ, with_path_info=False, 298b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik with_query_string=False) 299b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ['paste.throw_errors'] = True 300b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik started = [] 301b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def detect_start_response(status, headers, exc_info=None): 302b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 303b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return start_response(status, headers, exc_info) 304b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except: 305b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise 306b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 307b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik started.append(True) 308b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 309b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik __traceback_supplement__ = errormiddleware.Supplement, self, environ 310b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik app_iter = self.application(environ, detect_start_response) 311b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 312b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return_iter = list(app_iter) 313b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return return_iter 314b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik finally: 315b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if hasattr(app_iter, 'close'): 316b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik app_iter.close() 317b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except: 318b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exc_info = sys.exc_info() 319b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for expected in environ.get('paste.expected_exceptions', []): 320b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(exc_info[1], expected): 321b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise 322b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 323b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Tell the Registry to save its StackedObjectProxies current state 324b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # for later restoration 325b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik registry.restorer.save_registry_state(environ) 326b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 327b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik count = get_debug_count(environ) 328b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik view_uri = self.make_view_url(environ, base_path, count) 329b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if not started: 330b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik headers = [('content-type', 'text/html')] 331b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik headers.append(('X-Debug-URL', view_uri)) 332b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik start_response('500 Internal Server Error', 333b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik headers, 334b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exc_info) 335b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik msg = 'Debug at: %s\n' % view_uri 336b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if six.PY3: 337b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik msg = msg.encode('utf8') 338b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ['wsgi.errors'].write(msg) 339b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 340b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exc_data = collector.collect_exception(*exc_info) 341b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik debug_info = DebugInfo(count, exc_info, exc_data, base_path, 342b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ, view_uri) 343b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik assert count not in self.debug_infos 344b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.debug_infos[count] = debug_info 345b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 346b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if self.xmlhttp_key: 347b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik get_vars = request.parse_querystring(environ) 348b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if dict(get_vars).get(self.xmlhttp_key): 349b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exc_data = collector.collect_exception(*exc_info) 350b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik html = formatter.format_html( 351b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exc_data, include_hidden_frames=False, 352b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik include_reusable=False, show_extra_data=False) 353b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return [html] 354b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 355b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # @@: it would be nice to deal with bad content types here 356b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return debug_info.content() 357b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 358b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def exception_handler(self, exc_info, environ): 359b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik simple_html_error = False 360b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if self.xmlhttp_key: 361b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik get_vars = request.parse_querystring(environ) 362b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if dict(get_vars).get(self.xmlhttp_key): 363b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik simple_html_error = True 364b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return errormiddleware.handle_exception( 365b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik exc_info, environ['wsgi.errors'], 366b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik html=True, 367b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik debug_mode=True, 368b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik simple_html_error=simple_html_error) 369b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 370b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass DebugInfo(object): 371b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 372b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, counter, exc_info, exc_data, base_path, 373b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ, view_uri): 374b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.counter = counter 375b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.exc_data = exc_data 376b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.base_path = base_path 377b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.environ = environ 378b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.view_uri = view_uri 379b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.created = time.time() 380b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.exc_type, self.exc_value, self.tb = exc_info 381b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik __exception_formatter__ = 1 382b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.frames = [] 383b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik n = 0 384b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik tb = self.tb 385b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik while tb is not None and (limit is None or n < limit): 386b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if tb.tb_frame.f_locals.get('__exception_formatter__'): 387b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Stop recursion. @@: should make a fake ExceptionFrame 388b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik break 389b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.frames.append(tb) 390b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik tb = tb.tb_next 391b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik n += 1 392b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 393b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def json(self): 394b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """Return the JSON-able representation of this object""" 395b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return { 396b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'uri': self.view_uri, 397b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'created': time.strftime('%c', time.gmtime(self.created)), 398b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'created_timestamp': self.created, 399b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'exception_type': str(self.exc_type), 400b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'exception': str(self.exc_value), 401b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik } 402b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 403b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def frame(self, tbid): 404b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for frame in self.frames: 405b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if id(frame) == tbid: 406b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return frame 407b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 408b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik raise ValueError("No frame by id %s found from %r" % (tbid, self.frames)) 409b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 410b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def wsgi_application(self, environ, start_response): 411b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik start_response('200 OK', [('content-type', 'text/html')]) 412b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return self.content() 413b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 414b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def content(self): 415b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik html = format_eval_html(self.exc_data, self.base_path, self.counter) 416b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik head_html = (formatter.error_css + formatter.hide_display_js) 417b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik head_html += self.eval_javascript() 418b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik repost_button = make_repost_button(self.environ) 419b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik page = error_template % { 420b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'repost_button': repost_button or '', 421b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'head_html': head_html, 422b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'body': html} 423b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if six.PY3: 424b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik page = page.encode('utf8') 425b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return [page] 426b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 427b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def eval_javascript(self): 428b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik base_path = self.base_path + '/_debug' 429b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return ( 430b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '<script type="text/javascript" src="%s/media/MochiKit.packed.js">' 431b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '</script>\n' 432b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '<script type="text/javascript" src="%s/media/debug.js">' 433b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '</script>\n' 434b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '<script type="text/javascript">\n' 435b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'debug_base = %r;\n' 436b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'debug_count = %r;\n' 437b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '</script>\n' 438b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % (base_path, base_path, base_path, self.counter)) 439b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 440b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikclass EvalHTMLFormatter(formatter.HTMLFormatter): 441b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 442b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def __init__(self, base_path, counter, **kw): 443b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik super(EvalHTMLFormatter, self).__init__(**kw) 444b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.base_path = base_path 445b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self.counter = counter 446b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 447b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik def format_source_line(self, filename, frame): 448b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik line = formatter.HTMLFormatter.format_source_line( 449b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik self, filename, frame) 450b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return (line + 451b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ' <a href="#" class="switch_source" ' 452b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'tbid="%s" onClick="return showFrame(this)"> ' 453b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '<img src="%s/_debug/media/plus.jpg" border=0 width=9 ' 454b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'height=9> </a>' 455b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % (frame.tbid, self.base_path)) 456b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 457b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef make_table(items): 458b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if isinstance(items, dict): 459b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik items = items.items() 460b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik items.sort() 461b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik rows = [] 462b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik i = 0 463b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for name, value in items: 464b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik i += 1 465b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik out = StringIO() 466b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try: 467b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik pprint.pprint(value, out) 468b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik except Exception as e: 469b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik print('Error: %s' % e, file=out) 470b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = html_quote(out.getvalue()) 471b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if len(value) > 100: 472b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # @@: This can actually break the HTML :( 473b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # should I truncate before quoting? 474b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik orig_value = value 475b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = value[:100] 476b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value += '<a class="switch_source" style="background-color: #999" href="#" onclick="return expandLong(this)">...</a>' 477b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value += '<span style="display: none">%s</span>' % orig_value[100:] 478b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = formatter.make_wrappable(value) 479b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if i % 2: 480b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik attr = ' class="even"' 481b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 482b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik attr = ' class="odd"' 483b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik rows.append('<tr%s style="vertical-align: top;"><td>' 484b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '<b>%s</b></td><td style="overflow: auto">%s<td></tr>' 485b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % (attr, html_quote(name), 486b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik preserve_whitespace(value, quote=False))) 487b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return '<table>%s</table>' % ( 488b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '\n'.join(rows)) 489b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 490b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef format_eval_html(exc_data, base_path, counter): 491b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik short_formatter = EvalHTMLFormatter( 492b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik base_path=base_path, 493b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik counter=counter, 494b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik include_reusable=False) 495b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik short_er = short_formatter.format_collected_data(exc_data) 496b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik long_formatter = EvalHTMLFormatter( 497b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik base_path=base_path, 498b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik counter=counter, 499b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik show_hidden_frames=True, 500b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik show_extra_data=False, 501b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik include_reusable=False) 502b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik long_er = long_formatter.format_collected_data(exc_data) 503b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik text_er = formatter.format_text(exc_data, show_hidden_frames=True) 504b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if short_formatter.filter_frames(exc_data.frames) != \ 505b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik long_formatter.filter_frames(exc_data.frames): 506b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # Only display the full traceback when it differs from the 507b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # short version 508b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik full_traceback_html = """ 509b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <br> 510b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <script type="text/javascript"> 511b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik show_button('full_traceback', 'full traceback') 512b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik </script> 513b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <div id="full_traceback" class="hidden-data"> 514b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik %s 515b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik </div> 516b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ % long_er 517b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 518b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik full_traceback_html = '' 519b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 520b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return """ 521b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik %s 522b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik %s 523b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <br> 524b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <script type="text/javascript"> 525b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik show_button('text_version', 'text version') 526b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik </script> 527b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <div id="text_version" class="hidden-data"> 528b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <textarea style="width: 100%%" rows=10 cols=60>%s</textarea> 529b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik </div> 530b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ % (short_er, full_traceback_html, cgi.escape(text_er)) 531b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 532b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef make_repost_button(environ): 533b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik url = request.construct_url(environ) 534b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if environ['REQUEST_METHOD'] == 'GET': 535b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return ('<button onclick="window.location.href=%r">' 536b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 'Re-GET Page</button><br>' % url) 537b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik else: 538b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # @@: I'd like to reconstruct this, but I can't because 539b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # the POST body is probably lost at this point, and 540b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # I can't get it back :( 541b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return None 542b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # @@: Use or lose the following code block 543b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 544b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fields = [] 545b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik for name, value in wsgilib.parse_formvars( 546b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik environ, include_get_vars=False).items(): 547b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if hasattr(value, 'filename'): 548b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # @@: Arg, we'll just submit the body, and leave out 549b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik # the filename :( 550b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik value = value.value 551b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik fields.append( 552b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik '<input type="hidden" name="%s" value="%s">' 553b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik % (html_quote(name), html_quote(value))) 554b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return ''' 555b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<form action="%s" method="POST"> 556b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik%s 557b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<input type="submit" value="Re-POST Page"> 558b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik</form>''' % (url, '\n'.join(fields)) 559b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik""" 560b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 561b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 562b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef input_form(tbid, debug_info): 563b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return ''' 564b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<form action="#" method="POST" 565b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik onsubmit="return submitInput($(\'submit_%(tbid)s\'), %(tbid)s)"> 566b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<div id="exec-output-%(tbid)s" style="width: 95%%; 567b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik padding: 5px; margin: 5px; border: 2px solid #000; 568b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik display: none"></div> 569b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<input type="text" name="input" id="debug_input_%(tbid)s" 570b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik style="width: 100%%" 571b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik autocomplete="off" onkeypress="upArrow(this, event)"><br> 572b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<input type="submit" value="Execute" name="submitbutton" 573b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik onclick="return submitInput(this, %(tbid)s)" 574b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik id="submit_%(tbid)s" 575b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik input-from="debug_input_%(tbid)s" 576b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik output-to="exec-output-%(tbid)s"> 577b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<input type="submit" value="Expand" 578b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik onclick="return expandInput(this)"> 579b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik</form> 580b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik ''' % {'tbid': tbid} 581b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 582b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikerror_template = ''' 583b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<html> 584b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<head> 585b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik <title>Server Error</title> 586b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik %(head_html)s 587b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik</head> 588b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<body> 589b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 590b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<div id="error-area" style="display: none; background-color: #600; color: #fff; border: 2px solid black"> 591b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<div id="error-container"></div> 592b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik<button onclick="return clearError()">clear this</button> 593b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik</div> 594b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 595b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik%(repost_button)s 596b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 597b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik%(body)s 598b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 599b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik</body> 600b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik</html> 601b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik''' 602b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 603b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craikdef make_eval_exception(app, global_conf, xmlhttp_key=None): 604b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 605b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Wraps the application in an interactive debugger. 606b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 607b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik This debugger is a major security hole, and should only be 608b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik used during development. 609b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik 610b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik xmlhttp_key is a string that, if present in QUERY_STRING, 611b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik indicates that the request is an XMLHttp request, and the 612b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik Javascript/interactive debugger should not be returned. (If you 613b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik try to put the debugger somewhere with innerHTML, you will often 614b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik crash the browser) 615b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik """ 616b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik if xmlhttp_key is None: 617b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik xmlhttp_key = global_conf.get('xmlhttp_key', '_') 618b2cbf1594f8d6e4ba32d384cf379f62a74ed7654Chris Craik return EvalException(app, xmlhttp_key=xmlhttp_key) 619