Merge branch 'master' into Improving-documentation

This commit is contained in:
Adam Hopkins 2020-10-25 21:32:54 +02:00 committed by GitHub
commit 634b586df3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 45 additions and 19 deletions

View File

@ -7,8 +7,8 @@
""" """
from pathlib import Path from pathlib import Path
from sanic import Sanic, response
from sanic import Sanic, response
app = Sanic(__name__) app = Sanic(__name__)
@ -42,9 +42,7 @@ async def handler_file(request):
@app.route("/file_stream") @app.route("/file_stream")
async def handler_file_stream(request): async def handler_file_stream(request):
return await response.file_stream( return await response.file_stream(Path("../") / "setup.py", chunk_size=1024)
Path("../") / "setup.py", chunk_size=1024
)
@app.route("/stream", stream=True) @app.route("/stream", stream=True)

View File

@ -1 +1 @@
__version__ = "20.9.0" __version__ = "20.9.1"

View File

@ -350,6 +350,8 @@ class ASGIApp:
if name not in (b"Set-Cookie",) if name not in (b"Set-Cookie",)
] ]
response.asgi = True
if "content-length" not in response.headers and not isinstance( if "content-length" not in response.headers and not isinstance(
response, StreamingHTTPResponse response, StreamingHTTPResponse
): ):

View File

@ -22,6 +22,9 @@ except ImportError:
class BaseHTTPResponse: class BaseHTTPResponse:
def __init__(self):
self.asgi = False
def _encode_body(self, data): def _encode_body(self, data):
return data.encode() if hasattr(data, "encode") else data return data.encode() if hasattr(data, "encode") else data
@ -80,6 +83,8 @@ class StreamingHTTPResponse(BaseHTTPResponse):
content_type="text/plain; charset=utf-8", content_type="text/plain; charset=utf-8",
chunked=True, chunked=True,
): ):
super().__init__()
self.content_type = content_type self.content_type = content_type
self.streaming_fn = streaming_fn self.streaming_fn = streaming_fn
self.status = status self.status = status
@ -109,13 +114,14 @@ class StreamingHTTPResponse(BaseHTTPResponse):
""" """
if version != "1.1": if version != "1.1":
self.chunked = False self.chunked = False
headers = self.get_headers( if not getattr(self, "asgi", False):
version, headers = self.get_headers(
keep_alive=keep_alive, version,
keep_alive_timeout=keep_alive_timeout, keep_alive=keep_alive,
) keep_alive_timeout=keep_alive_timeout,
await self.protocol.push_data(headers) )
await self.protocol.drain() await self.protocol.push_data(headers)
await self.protocol.drain()
await self.streaming_fn(self) await self.streaming_fn(self)
if self.chunked: if self.chunked:
await self.protocol.push_data(b"0\r\n\r\n") await self.protocol.push_data(b"0\r\n\r\n")
@ -143,6 +149,8 @@ class HTTPResponse(BaseHTTPResponse):
content_type=None, content_type=None,
body_bytes=b"", body_bytes=b"",
): ):
super().__init__()
self.content_type = content_type self.content_type = content_type
self.body = body_bytes if body is None else self._encode_body(body) self.body = body_bytes if body is None else self._encode_body(body)
self.status = status self.status = status

View File

@ -80,13 +80,13 @@ requirements = [
ujson, ujson,
"aiofiles>=0.3.0", "aiofiles>=0.3.0",
"websockets>=8.1,<9.0", "websockets>=8.1,<9.0",
"multidict>=4.0,<5.0", "multidict==5.0.0",
"httpx==0.15.4", "httpx==0.15.4",
] ]
tests_require = [ tests_require = [
"pytest==5.2.1", "pytest==5.2.1",
"multidict>=4.0,<5.0", "multidict==5.0.0",
"gunicorn", "gunicorn",
"pytest-cov", "pytest-cov",
"httpcore==0.3.0", "httpcore==0.3.0",
@ -96,6 +96,7 @@ tests_require = [
"pytest-sanic", "pytest-sanic",
"pytest-sugar", "pytest-sugar",
"pytest-benchmark", "pytest-benchmark",
"pytest-dependency",
] ]
docs_require = [ docs_require = [

View File

@ -10,7 +10,7 @@ from sanic.utils import load_module_from_file_location
@pytest.fixture @pytest.fixture
def loaded_module_from_file_location(): def loaded_module_from_file_location():
return load_module_from_file_location( return load_module_from_file_location(
str(Path(__file__).parent / "static/app_test_config.py") str(Path(__file__).parent / "static" / "app_test_config.py")
) )
@ -20,10 +20,11 @@ def test_load_module_from_file_location(loaded_module_from_file_location):
@pytest.mark.dependency(depends=["test_load_module_from_file_location"]) @pytest.mark.dependency(depends=["test_load_module_from_file_location"])
def test_loaded_module_from_file_location_name( def test_loaded_module_from_file_location_name(loaded_module_from_file_location,):
loaded_module_from_file_location, name = loaded_module_from_file_location.__name__
): if "C:\\" in name:
assert loaded_module_from_file_location.__name__ == "app_test_config" name = name.split("\\")[-1]
assert name == "app_test_config"
def test_load_module_from_file_location_with_non_existing_env_variable(): def test_load_module_from_file_location_with_non_existing_env_variable():

View File

@ -235,6 +235,12 @@ def test_chunked_streaming_returns_correct_content(streaming_app):
assert response.text == "foo,bar" assert response.text == "foo,bar"
@pytest.mark.asyncio
async def test_chunked_streaming_returns_correct_content_asgi(streaming_app):
request, response = await streaming_app.asgi_client.get("/")
assert response.text == "4\r\nfoo,\r\n3\r\nbar\r\n0\r\n\r\n"
def test_non_chunked_streaming_adds_correct_headers(non_chunked_streaming_app): def test_non_chunked_streaming_adds_correct_headers(non_chunked_streaming_app):
request, response = non_chunked_streaming_app.test_client.get("/") request, response = non_chunked_streaming_app.test_client.get("/")
assert "Transfer-Encoding" not in response.headers assert "Transfer-Encoding" not in response.headers
@ -242,6 +248,16 @@ def test_non_chunked_streaming_adds_correct_headers(non_chunked_streaming_app):
assert response.headers["Content-Length"] == "7" assert response.headers["Content-Length"] == "7"
@pytest.mark.asyncio
async def test_non_chunked_streaming_adds_correct_headers_asgi(
non_chunked_streaming_app,
):
request, response = await non_chunked_streaming_app.asgi_client.get("/")
assert "Transfer-Encoding" not in response.headers
assert response.headers["Content-Type"] == "text/csv"
assert response.headers["Content-Length"] == "7"
def test_non_chunked_streaming_returns_correct_content( def test_non_chunked_streaming_returns_correct_content(
non_chunked_streaming_app, non_chunked_streaming_app,
): ):