added exception chain rendering in debug #675

This commit is contained in:
Alec Buckenheimer 2017-05-01 12:56:33 -04:00
parent 158a94d34c
commit 69511c2783
3 changed files with 75 additions and 13 deletions

View File

@ -47,6 +47,10 @@ TRACEBACK_STYLE = '''
padding: 5px 10px; padding: 5px 10px;
} }
.tb-border {
padding-top: 20px;
}
.frame-descriptor { .frame-descriptor {
background-color: #e2eafb; background-color: #e2eafb;
} }
@ -63,12 +67,9 @@ TRACEBACK_WRAPPER_HTML = '''
{style} {style}
</head> </head>
<body> <body>
<h1>{exc_name}</h1> {inner_html}
<h3><code>{exc_value}</code></h3> <div class="summary">
<div class="tb-wrapper"> <p>
<p class="tb-header">Traceback (most recent call last):</p>
{frame_html}
<p class="summary">
<b>{exc_name}: {exc_value}</b> <b>{exc_name}: {exc_value}</b>
while handling path <code>{path}</code> while handling path <code>{path}</code>
</p> </p>
@ -77,6 +78,24 @@ TRACEBACK_WRAPPER_HTML = '''
</html> </html>
''' '''
TRACEBACK_WRAPPER_INNER_HTML = '''
<h1>{exc_name}</h1>
<h3><code>{exc_value}</code></h3>
<div class="tb-wrapper">
<p class="tb-header">Traceback (most recent call last):</p>
{frame_html}
</div>
'''
TRACEBACK_BORDER = '''
<div class="tb-border">
<b><i>
The above exception was the direct cause of the
following exception:
</i></b>
</div>
'''
TRACEBACK_LINE_HTML = ''' TRACEBACK_LINE_HTML = '''
<div class="frame-line"> <div class="frame-line">
<p class="frame-descriptor"> <p class="frame-descriptor">

View File

@ -9,7 +9,9 @@ from sanic.exceptions import (
SanicException, SanicException,
TRACEBACK_LINE_HTML, TRACEBACK_LINE_HTML,
TRACEBACK_STYLE, TRACEBACK_STYLE,
TRACEBACK_WRAPPER_HTML) TRACEBACK_WRAPPER_HTML,
TRACEBACK_WRAPPER_INNER_HTML,
TRACEBACK_BORDER)
from sanic.log import log from sanic.log import log
from sanic.response import text, html from sanic.response import text, html
@ -24,19 +26,31 @@ class ErrorHandler:
self.cached_handlers = {} self.cached_handlers = {}
self.debug = False self.debug = False
def _render_traceback_html(self, exception, request): def _render_exception(self, exception):
exc_type, exc_value, tb = sys.exc_info() frames = extract_tb(exception.__traceback__)
frames = extract_tb(tb)
frame_html = [] frame_html = []
for frame in frames: for frame in frames:
frame_html.append(TRACEBACK_LINE_HTML.format(frame)) frame_html.append(TRACEBACK_LINE_HTML.format(frame))
return TRACEBACK_WRAPPER_INNER_HTML.format(
exc_name=exception.__class__.__name__,
exc_value=exception,
frame_html=''.join(frame_html))
def _render_traceback_html(self, exception, request):
exc_type, exc_value, tb = sys.exc_info()
exceptions = []
while exc_value:
exceptions.append(self._render_exception(exc_value))
exc_value = exc_value.__cause__
return TRACEBACK_WRAPPER_HTML.format( return TRACEBACK_WRAPPER_HTML.format(
style=TRACEBACK_STYLE, style=TRACEBACK_STYLE,
exc_name=exc_type.__name__, exc_name=exception.__class__.__name__,
exc_value=exc_value, exc_value=exception,
frame_html=''.join(frame_html), inner_html=TRACEBACK_BORDER.join(reversed(exceptions)),
path=request.path) path=request.path)
def add(self, exception, handler): def add(self, exception, handler):

View File

@ -35,6 +35,15 @@ def handler_5(request):
raise CustomServerError('Custom server error') raise CustomServerError('Custom server error')
@exception_handler_app.route('/6/<arg:int>')
def handler_6(request, arg):
try:
foo = 1 / arg
except Exception as e:
raise e from ValueError("{}".format(arg))
return text(foo)
@exception_handler_app.exception(NotFound, ServerError) @exception_handler_app.exception(NotFound, ServerError)
def handler_exception(request, exception): def handler_exception(request, exception):
return text("OK") return text("OK")
@ -84,6 +93,26 @@ def test_inherited_exception_handler():
assert response.status == 200 assert response.status == 200
def test_chained_exception_handler():
request, response = exception_handler_app.test_client.get(
'/6/0', debug=True)
assert response.status == 500
soup = BeautifulSoup(response.body, 'html.parser')
html = str(soup)
assert 'response = handler(request, *args, **kwargs)' in html
assert 'handler_6' in html
assert 'foo = 1 / arg' in html
assert 'ValueError' in html
assert 'The above exception was the direct cause' in html
summary_text = " ".join(soup.select('.summary')[0].text.split())
assert (
"ZeroDivisionError: division by zero "
"while handling path /6/0") == summary_text
def test_exception_handler_lookup(): def test_exception_handler_lookup():
class CustomError(Exception): class CustomError(Exception):
pass pass