Merge branch 'html-error-handling-suggestions' into niceback-error-handling

This commit is contained in:
L. Karkkainen 2023-02-06 13:02:31 +00:00
commit 3ddbda61d9
3 changed files with 90 additions and 25 deletions

View File

@ -1,22 +1,36 @@
from .base import BasePage from typing import Any, Mapping
from sanic.request import Request
from contextlib import suppress
from html5tagger import E
from tracerite import html_traceback, inspector
import tracerite.html 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 # Avoid showing the request in the traceback variable inspectors
inspector.blacklist_types += Request, inspector.blacklist_types += (Request,)
class ErrorPage(BasePage): class ErrorPage(BasePage):
STYLE_APPEND = tracerite.html.style 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__() super().__init__()
# Internal server errors come with the text of the exception, which we don't want to show to the user. # 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. # FIXME: This needs to be done some place else but I am not digging into that now.
if "Internal Server Error" in title: if "Internal Server Error" in title:
text = "The application encountered an unexpected error and could not continue." 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.title = title
self.text = text self.text = text
self.request = request self.request = request
@ -35,26 +49,53 @@ class ErrorPage(BasePage):
route_name = "[route not found]" route_name = "[route not found]"
with self.doc.main: with self.doc.main:
self.doc.h1(f"⚠️ {self.title}").p(self.text) self.doc.h1(f"⚠️ {self.title}").p(self.text)
context = getattr(self.exc, "context", None) or {} # Show context details if available on the exception
if debug: context = getattr(self.exc, "context", None)
context.update(getattr(self.exc, "extra", None) or {})
# Show context and extra details if available on the exception
if context: if context:
# Printing values may easily cause a new exception, so suppress it self._key_value_table(
with self.doc.table(id="exception-context"), suppress(Exception): "Issue context", "exception-context", context
for k, v in context.items(): )
self.doc.tr.td(k).td(v)
if not debug: if not debug:
return 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"): 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: if self.exc:
self.doc.h2(f"Exception in {route_name}:") self.doc.h2(f"Exception in {route_name}:")
# skip_outmost=1 to hide Sanic.handle_request # 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}") self._key_value_table(
with self.doc.table(id="request-headers"): f"{self.request.method} {self.request.path}",
for k, v in self.request.headers.items(): "request-headers",
self.doc.tr.td(f"{k}:", class_="nobr").td(v) 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"
)

View File

@ -1,4 +1,3 @@
/** BasePage **/ /** BasePage **/
html { html {
font: 16px sans-serif; font: 16px sans-serif;
@ -9,6 +8,11 @@ html {
body { body {
margin: 0; margin: 0;
font-size: 1.25rem; font-size: 1.25rem;
--sanic: #ff0d68;
--sanic-blue: #0092FF;
--sanic-yellow: #FFE900;
--sanic-purple: #833FE3;
--sanic-green: #16DB93;
} }
body>* { body>* {

View File

@ -1,2 +1,22 @@
/** ErrorPage **/ /** 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;
}