From eb9ce5b05eff01256c04872d8ce478cc7612a7fb Mon Sep 17 00:00:00 2001 From: marcosschroh Date: Fri, 14 Jun 2019 00:57:54 +0200 Subject: [PATCH] feat(ServerErrorMiddleware): Frames have been reverted. Style improved. Collapse added on every frame. Context added --- starlette/middleware/errors.py | 104 ++++++++++++++++++++++++++++----- 1 file changed, 88 insertions(+), 16 deletions(-) diff --git a/starlette/middleware/errors.py b/starlette/middleware/errors.py index 54f5fd2a..0d153ac4 100644 --- a/starlette/middleware/errors.py +++ b/starlette/middleware/errors.py @@ -1,4 +1,5 @@ import asyncio +import inspect import traceback import typing @@ -8,6 +9,9 @@ from starlette.responses import HTMLResponse, PlainTextResponse, Response from starlette.types import ASGIApp, Message, Receive, Scope, Send STYLES = """ +p { + color: #211c1c; +} .traceback-container { border: 1px solid #038BB8; } @@ -18,20 +22,53 @@ STYLES = """ font-size: 20px; margin-top: 0px; } -.traceback-content { - padding: 5px 0px 20px 20px; -} .frame-line { + padding-left: 10px; +} +.center-line { + background-color: #038BB8; + color: #f9f6e1; + padding: 5px 0px 5px 5px; +} +.lineno { + margin-right: 5px; +} +.frame-filename { font-weight: unset; - padding: 10px 10px 10px 20px; + padding: 10px 10px 10px 0px; background-color: #E4F4FD; - margin-left: 10px; margin-right: 10px; font: #394D54; color: #191f21; font-size: 17px; border: 1px solid #c7dce8; } +.collapse-btn { + float: right; + padding: 0px 5px 1px 5px; + border: solid 1px #96aebb; + cursor: pointer; +} +.collapsed { + display: none; +} +""" + +JS = """ + """ TEMPLATE = """ @@ -45,23 +82,36 @@ TEMPLATE = """

500 Server Error

{error}

-
-

Traceback

-
{exc_html}
+
+

Traceback

+
{exc_html}
+ {js} """ FRAME_TEMPLATE = """
- File `{frame_filename}`, +

File {frame_filename}, line {frame_lineno}, in {frame_name} -

{frame_line}

+ +

+
{code_context}
""" +LINE = """ +

+{lineno}. {line}

+""" + +CENTER_LINE = """ +

+{lineno}. {line}

+""" + class ServerErrorMiddleware: """ @@ -121,25 +171,47 @@ class ServerErrorMiddleware: # to optionally raise the error within the test case. raise exc from None - def generate_frame_html(self, frame: traceback.FrameSummary) -> str: + def format_line( + self, position: int, line: str, frame_lineno: int, center_lineno: int + ) -> str: + values = { + "line": line.replace(" ", " "), + "lineno": frame_lineno + (position - center_lineno), + } + + if position != center_lineno: + return LINE.format(**values) + return CENTER_LINE.format(**values) + + def generate_frame_html(self, frame: inspect.FrameInfo, center_lineno: int) -> str: + code_context = "".join( + self.format_line(context_position, line, frame.lineno, center_lineno) + for context_position, line in enumerate(frame.code_context) + ) + values = { "frame_filename": frame.filename, "frame_lineno": frame.lineno, - "frame_name": frame.name, - "frame_line": frame.line, + "frame_name": frame.function, + "code_context": code_context, } return FRAME_TEMPLATE.format(**values) - def generate_html(self, exc: Exception) -> str: + def generate_html(self, exc: Exception, limit: int = 7) -> str: traceback_obj = traceback.TracebackException.from_exception( exc, capture_locals=True ) + frames = inspect.getinnerframes( + traceback_obj.exc_traceback, limit # type: ignore + ) + + center_lineno = int((limit - 1) / 2) exc_html = "".join( - self.generate_frame_html(frame) for frame in traceback_obj.stack + self.generate_frame_html(frame, center_lineno) for frame in reversed(frames) ) error = f"{traceback_obj.exc_type.__name__}: {traceback_obj}" - return TEMPLATE.format(styles=STYLES, error=error, exc_html=exc_html) + return TEMPLATE.format(styles=STYLES, js=JS, error=error, exc_html=exc_html) def generate_plain_text(self, exc: Exception) -> str: return "".join(traceback.format_tb(exc.__traceback__)) -- 2.47.2