Nicer traceback formatting (#2667)

Co-authored-by: L. Kärkkäinen <98187+Tronic@users.noreply.github.com>
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
Co-authored-by: L. Karkkainen <tronic@users.noreply.github.com>
Co-authored-by: SML <smlbiobot@gmail.com>
This commit is contained in:
L. Kärkkäinen
2023-03-06 19:24:12 +00:00
committed by GitHub
parent 259e458847
commit a5d7d03413
16 changed files with 442 additions and 195 deletions

View File

@@ -4,7 +4,7 @@ import pytest
from sanic import Sanic
from sanic.config import Config
from sanic.errorpages import TextRenderer, guess_mime, exception_response
from sanic.errorpages import TextRenderer, exception_response, guess_mime
from sanic.exceptions import NotFound, SanicException
from sanic.handlers import ErrorHandler
from sanic.request import Request
@@ -57,7 +57,6 @@ def app():
raise Exception
return json({}) if param == "json" else html("")
return app
@@ -314,7 +313,6 @@ def test_fallback_with_content_type_mismatch_accept(app):
("*/*", "application/json", "application/json"),
# App wants text/plain but accept has equal entries for it
("text/*,*/plain", None, "text/plain; charset=utf-8"),
),
)
def test_combinations_for_auto(fake_request, accept, content_type, expected):
@@ -428,25 +426,83 @@ def test_config_fallback_bad_value(app):
@pytest.mark.parametrize(
"route_format,fallback,accept,expected",
(
("json", "html", "*/*", "The client accepts */*, using 'json' from fakeroute"),
("json", "auto", "text/html,*/*;q=0.8", "The client accepts text/html, using 'html' from any"),
("json", "json", "text/html,*/*;q=0.8", "The client accepts */*;q=0.8, using 'json' from fakeroute"),
("", "html", "text/*,*/plain", "The client accepts text/*, using 'html' from FALLBACK_ERROR_FORMAT"),
("", "json", "text/*,*/*", "The client accepts */*, using 'json' from FALLBACK_ERROR_FORMAT"),
("", "auto", "*/*,application/json;q=0.5", "The client accepts */*, using 'json' from request.accept"),
("", "auto", "*/*", "The client accepts */*, using 'json' from content-type"),
("", "auto", "text/html,text/plain", "The client accepts text/plain, using 'text' from any"),
("", "auto", "text/html,text/plain;q=0.9", "The client accepts text/html, using 'html' from any"),
("html", "json", "application/xml", "No format found, the client accepts [application/xml]"),
(
"json",
"html",
"*/*",
"The client accepts */*, using 'json' from fakeroute",
),
(
"json",
"auto",
"text/html,*/*;q=0.8",
"The client accepts text/html, using 'html' from any",
),
(
"json",
"json",
"text/html,*/*;q=0.8",
"The client accepts */*;q=0.8, using 'json' from fakeroute",
),
(
"",
"html",
"text/*,*/plain",
"The client accepts text/*, using 'html' from FALLBACK_ERROR_FORMAT",
),
(
"",
"json",
"text/*,*/*",
"The client accepts */*, using 'json' from FALLBACK_ERROR_FORMAT",
),
(
"",
"auto",
"*/*,application/json;q=0.5",
"The client accepts */*, using 'json' from request.accept",
),
(
"",
"auto",
"*/*",
"The client accepts */*, using 'json' from content-type",
),
(
"",
"auto",
"text/html,text/plain",
"The client accepts text/plain, using 'text' from any",
),
(
"",
"auto",
"text/html,text/plain;q=0.9",
"The client accepts text/html, using 'html' from any",
),
(
"html",
"json",
"application/xml",
"No format found, the client accepts [application/xml]",
),
("", "auto", "*/*", "The client accepts */*, using 'text' from any"),
("", "", "*/*", "No format found, the client accepts [*/*]"),
# DEPRECATED: remove in 24.3
("", "auto", "*/*", "The client accepts */*, using 'json' from request.json"),
(
"",
"auto",
"*/*",
"The client accepts */*, using 'json' from request.json",
),
),
)
def test_guess_mime_logging(caplog, fake_request, route_format, fallback, accept, expected):
def test_guess_mime_logging(
caplog, fake_request, route_format, fallback, accept, expected
):
class FakeObject:
pass
fake_request.route = FakeObject()
fake_request.route.name = "fakeroute"
fake_request.route.extra = FakeObject()
@@ -466,6 +522,8 @@ def test_guess_mime_logging(caplog, fake_request, route_format, fallback, accept
with caplog.at_level(logging.DEBUG, logger="sanic.root"):
guess_mime(fake_request, fallback)
logmsg, = [r.message for r in caplog.records if r.funcName == "guess_mime"]
(logmsg,) = [
r.message for r in caplog.records if r.funcName == "guess_mime"
]
assert logmsg == expected

View File

@@ -23,11 +23,11 @@ from sanic.exceptions import (
from sanic.response import text
def dl_to_dict(soup, css_class):
def dl_to_dict(soup, dl_id):
keys, values = [], []
for dl in soup.find_all("dl", {"class": css_class}):
for dl in soup.find_all("dl", {"id": dl_id}):
for dt in dl.find_all("dt"):
keys.append(dt.text.strip())
keys.append(dt.text.split(":", 1)[0])
for dd in dl.find_all("dd"):
values.append(dd.text.strip())
return dict(zip(keys, values))
@@ -194,10 +194,7 @@ def test_handled_unhandled_exception(exception_app):
assert "Internal Server Error" in soup.h1.text
message = " ".join(soup.p.text.split())
assert message == (
"The server encountered an internal error and "
"cannot complete your request."
)
assert "The application encountered an unexpected error" in message
def test_exception_in_exception_handler(exception_app):
@@ -299,7 +296,7 @@ def test_contextual_exception_context(debug):
_, response = app.test_client.post("/coffee/html", debug=debug)
soup = BeautifulSoup(response.body, "html.parser")
dl = dl_to_dict(soup, "context")
dl = dl_to_dict(soup, "exception-context")
assert response.status == 418
assert "Sorry, I cannot brew coffee" in soup.find("p").text
assert dl == {"foo": "bar"}
@@ -340,7 +337,7 @@ def test_contextual_exception_extra(debug):
_, response = app.test_client.post("/coffee/html", debug=debug)
soup = BeautifulSoup(response.body, "html.parser")
dl = dl_to_dict(soup, "extra")
dl = dl_to_dict(soup, "exception-extra")
assert response.status == 418
assert "Found bar" in soup.find("p").text
if debug:

View File

@@ -123,10 +123,10 @@ def test_html_traceback_output_in_debug_mode(exception_handler_app: Sanic):
assert "handler_4" in html
assert "foo = bar" in html
summary_text = " ".join(soup.select(".summary")[0].text.split())
assert (
"NameError: name 'bar' is not defined while handling path /4"
) == summary_text
summary_text = soup.select("h3")[0].text
assert "NameError: name 'bar' is not defined" == summary_text
request_text = soup.select("h2")[-1].text
assert "GET /4" == request_text
def test_inherited_exception_handler(exception_handler_app: Sanic):
@@ -146,11 +146,10 @@ def test_chained_exception_handler(exception_handler_app: Sanic):
assert "handler_6" in html
assert "foo = 1 / arg" in html
assert "ValueError" in html
assert "GET /6" in html
summary_text = " ".join(soup.select(".summary")[0].text.split())
assert (
"ZeroDivisionError: division by zero while handling path /6/0"
) == summary_text
summary_text = soup.select("h3")[0].text
assert "ZeroDivisionError: division by zero" == summary_text
def test_exception_handler_lookup(exception_handler_app: Sanic):

View File

@@ -50,7 +50,7 @@ def raised_ceiling():
),
(
'form-data; name="foo"; value="%22\\%0D%0A"',
("form-data", {"name": "foo", "value": '\"\\\n'})
("form-data", {"name": "foo", "value": '"\\\n'}),
),
# <input type=file name="foo&quot;;bar\"> with Unicode filename!
(