More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae01.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
This commit is contained in:
committed by
Stephen Sadowski
parent
e908ca8cef
commit
bffdb3b5c2
@@ -11,7 +11,7 @@ import pytest
|
||||
from sanic import Blueprint, Sanic
|
||||
from sanic.exceptions import ServerError
|
||||
from sanic.request import DEFAULT_HTTP_CONTENT_TYPE, Request, RequestParameters
|
||||
from sanic.response import json, text
|
||||
from sanic.response import html, json, text
|
||||
from sanic.testing import ASGI_HOST, HOST, PORT
|
||||
|
||||
|
||||
@@ -72,6 +72,41 @@ def test_text(app):
|
||||
assert response.text == "Hello"
|
||||
|
||||
|
||||
def test_html(app):
|
||||
class Foo:
|
||||
def __html__(self):
|
||||
return "<h1>Foo</h1>"
|
||||
|
||||
def _repr_html_(self):
|
||||
return "<h1>Foo object repr</h1>"
|
||||
|
||||
class Bar:
|
||||
def _repr_html_(self):
|
||||
return "<h1>Bar object repr</h1>"
|
||||
|
||||
@app.route("/")
|
||||
async def handler(request):
|
||||
return html("<h1>Hello</h1>")
|
||||
|
||||
@app.route("/foo")
|
||||
async def handler(request):
|
||||
return html(Foo())
|
||||
|
||||
@app.route("/bar")
|
||||
async def handler(request):
|
||||
return html(Bar())
|
||||
|
||||
request, response = app.test_client.get("/")
|
||||
assert response.content_type == "text/html; charset=utf-8"
|
||||
assert response.text == "<h1>Hello</h1>"
|
||||
|
||||
request, response = app.test_client.get("/foo")
|
||||
assert response.text == "<h1>Foo</h1>"
|
||||
|
||||
request, response = app.test_client.get("/bar")
|
||||
assert response.text == "<h1>Bar object repr</h1>"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_text_asgi(app):
|
||||
@app.route("/")
|
||||
|
||||
@@ -20,6 +20,7 @@ from sanic.response import (
|
||||
json,
|
||||
raw,
|
||||
stream,
|
||||
text,
|
||||
)
|
||||
from sanic.response import empty
|
||||
from sanic.server import HttpProtocol
|
||||
@@ -35,7 +36,7 @@ def test_response_body_not_a_string(app):
|
||||
|
||||
@app.route("/hello")
|
||||
async def hello_route(request):
|
||||
return HTTPResponse(body=random_num)
|
||||
return text(random_num)
|
||||
|
||||
request, response = app.test_client.get("/hello")
|
||||
assert response.text == str(random_num)
|
||||
|
||||
Reference in New Issue
Block a user