diff --git a/sanic/pages/error.py b/sanic/pages/error.py index 90a0102a..4c1a0ce2 100644 --- a/sanic/pages/error.py +++ b/sanic/pages/error.py @@ -1,22 +1,36 @@ -from .base import BasePage -from sanic.request import Request -from contextlib import suppress -from html5tagger import E -from tracerite import html_traceback, inspector +from typing import Any, Mapping + import tracerite.html +from html5tagger import E +from tracerite import html_traceback, inspector + +from sanic.request import Request + +from .base import BasePage + + # Avoid showing the request in the traceback variable inspectors -inspector.blacklist_types += Request, +inspector.blacklist_types += (Request,) + class ErrorPage(BasePage): STYLE_APPEND = tracerite.html.style - def __init__(self, title: str, text: str, request: Request, exc: Exception, full: bool) -> None: + + def __init__( + self, + title: str, + text: str, + request: Request, + exc: Exception, + full: bool, + ) -> None: super().__init__() # Internal server errors come with the text of the exception, which we don't want to show to the user. # FIXME: This needs to be done some place else but I am not digging into that now. if "Internal Server Error" in title: text = "The application encountered an unexpected error and could not continue." - self.TITLE = E(f"App {request.app.name} cannot handle your request") + self.TITLE = E.strong(request.app.name)(" cannot handle your request") self.title = title self.text = text self.request = request @@ -35,26 +49,53 @@ class ErrorPage(BasePage): route_name = "[route not found]" with self.doc.main: self.doc.h1(f"⚠️ {self.title}").p(self.text) - context = getattr(self.exc, "context", None) or {} - if debug: - context.update(getattr(self.exc, "extra", None) or {}) - # Show context and extra details if available on the exception + # Show context details if available on the exception + context = getattr(self.exc, "context", None) if context: - # Printing values may easily cause a new exception, so suppress it - with self.doc.table(id="exception-context"), suppress(Exception): - for k, v in context.items(): - self.doc.tr.td(k).td(v) + self._key_value_table( + "Issue context", "exception-context", context + ) + if not debug: return - # Show additional details in debug mode, open by default for 500 errors + # Show additional details in debug mode, + # open by default for 500 errors with self.doc.details(open=self.full, class_="smalltext"): - self.doc.summary("Details for developers (Sanic debug mode only)") + # Show extra details if available on the exception + extra = getattr(self.exc, "extra", None) + if extra: + self._key_value_table( + "Exception extra data", "exception-extra", extra + ) + + self.doc.summary( + "Details for developers (Sanic debug mode only)" + ) if self.exc: self.doc.h2(f"Exception in {route_name}:") # skip_outmost=1 to hide Sanic.handle_request - self.doc(html_traceback(self.exc, skip_outmost=1, include_js_css=False)) + self.doc( + html_traceback( + self.exc, skip_outmost=1, include_js_css=False + ) + ) - self.doc.h2(f"{self.request.method} {self.request.path}") - with self.doc.table(id="request-headers"): - for k, v in self.request.headers.items(): - self.doc.tr.td(f"{k}:", class_="nobr").td(v) + self._key_value_table( + f"{self.request.method} {self.request.path}", + "request-headers", + self.request.headers, + ) + + def _key_value_table( + self, title: str, table_id: str, data: Mapping[str, Any] + ) -> None: + with self.doc.table(id=table_id, class_="key-value-table smalltext"): + self.doc.caption(title) + for key, value in data.items(): + try: + self.doc.tr.td(key, class_="nobr key").td(value) + # Printing values may cause a new exception, so suppress it + except Exception: + self.doc.tr.td(key, class_="nobr key").td.em( + "Unable to display value" + ) diff --git a/sanic/pages/styles/BasePage.css b/sanic/pages/styles/BasePage.css index adcbf7b9..f31ff333 100644 --- a/sanic/pages/styles/BasePage.css +++ b/sanic/pages/styles/BasePage.css @@ -1,4 +1,3 @@ - /** BasePage **/ html { font: 16px sans-serif; @@ -9,6 +8,11 @@ html { body { margin: 0; font-size: 1.25rem; + --sanic: #ff0d68; + --sanic-blue: #0092FF; + --sanic-yellow: #FFE900; + --sanic-purple: #833FE3; + --sanic-green: #16DB93; } body>* { diff --git a/sanic/pages/styles/ErrorPage.css b/sanic/pages/styles/ErrorPage.css index 6ce60c1a..6a3212f3 100644 --- a/sanic/pages/styles/ErrorPage.css +++ b/sanic/pages/styles/ErrorPage.css @@ -1,2 +1,22 @@ /** ErrorPage **/ -summary { color: #888; } +summary { + color: #888; +} + +.tracerite { + --color-var: var(--sanic-blue); + --color-val: var(--sanic-green); + --color-type: var(--sanic-purple); +} + +.key-value-table { + margin: 2rem 0; +} + +.key-value-table caption { + caption-side: top; + text-align: left; + font-size: 1.25rem; + font-weight: 700; + padding-bottom: 0.5rem; +}