Merge pull request #677 from abuckenheimer/master
added exception chain rendering in debug #675
This commit is contained in:
		| @@ -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"> | ||||||
|   | |||||||
| @@ -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): | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Eli Uriegas
					Eli Uriegas