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:
		 L. Kärkkäinen
					L. Kärkkäinen
				
			
				
					committed by
					
						 Stephen Sadowski
						Stephen Sadowski
					
				
			
			
				
	
			
			
			 Stephen Sadowski
						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("/") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user