Skip to content

Commit

Permalink
Merge pull request #1614 from huge-success/asgi-custom-request
Browse files Browse the repository at this point in the history
Add custom request support to ASGI mode; fix a couple tests
  • Loading branch information
yunstanford authored Jul 6, 2019
2 parents 650ab61 + c7f2399 commit 1eaa2e3
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 40 deletions.
55 changes: 27 additions & 28 deletions sanic/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def create_websocket_connection(
self._websocket_connection = WebSocketConnection(send, receive)
return self._websocket_connection

def add_task(self) -> None: # noqa
def add_task(self) -> None:
raise NotImplementedError

async def send(self, data) -> None:
Expand Down Expand Up @@ -119,42 +119,40 @@ def __init__(self, asgi_app: "ASGIApp") -> None:
"the ASGI server is stopped."
)

# async def pre_startup(self) -> None:
# for handler in self.asgi_app.sanic_app.listeners[
# "before_server_start"
# ]:
# response = handler(
# self.asgi_app.sanic_app, self.asgi_app.sanic_app.loop
# )
# if isawaitable(response):
# await response

async def startup(self) -> None:
for handler in self.asgi_app.sanic_app.listeners[
"before_server_start"
]:
response = handler(
self.asgi_app.sanic_app, self.asgi_app.sanic_app.loop
)
if isawaitable(response):
await response
"""
Gather the listeners to fire on server start.
Because we are using a third-party server and not Sanic server, we do
not have access to fire anything BEFORE the server starts.
Therefore, we fire before_server_start and after_server_start
in sequence since the ASGI lifespan protocol only supports a single
startup event.
"""
listeners = self.asgi_app.sanic_app.listeners.get(
"before_server_start", []
) + self.asgi_app.sanic_app.listeners.get("after_server_start", [])

for handler in self.asgi_app.sanic_app.listeners["after_server_start"]:
for handler in listeners:
response = handler(
self.asgi_app.sanic_app, self.asgi_app.sanic_app.loop
)
if isawaitable(response):
await response

async def shutdown(self) -> None:
for handler in self.asgi_app.sanic_app.listeners["before_server_stop"]:
response = handler(
self.asgi_app.sanic_app, self.asgi_app.sanic_app.loop
)
if isawaitable(response):
await response
"""
Gather the listeners to fire on server stop.
Because we are using a third-party server and not Sanic server, we do
not have access to fire anything AFTER the server stops.
Therefore, we fire before_server_stop and after_server_stop
in sequence since the ASGI lifespan protocol only supports a single
shutdown event.
"""
listeners = self.asgi_app.sanic_app.listeners.get(
"before_server_stop", []
) + self.asgi_app.sanic_app.listeners.get("after_server_stop", [])

for handler in self.asgi_app.sanic_app.listeners["after_server_stop"]:
for handler in listeners:
response = handler(
self.asgi_app.sanic_app, self.asgi_app.sanic_app.loop
)
Expand Down Expand Up @@ -223,7 +221,8 @@ async def create(
# TODO:
# - close connection

instance.request = Request(
request_class = sanic_app.request_class or Request
instance.request = request_class(
url_bytes,
headers,
version,
Expand Down
28 changes: 28 additions & 0 deletions tests/test_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
import pytest
import uvicorn

from sanic import Sanic
from sanic.asgi import MockTransport
from sanic.exceptions import InvalidUsage
from sanic.request import Request
from sanic.response import text
from sanic.websocket import WebSocketConnection


Expand Down Expand Up @@ -201,3 +204,28 @@ def test_improper_websocket_connection(transport, send, receive):
transport.create_websocket_connection(send, receive)
connection = transport.get_websocket_connection()
assert isinstance(connection, WebSocketConnection)


@pytest.mark.asyncio
async def test_request_class_regular(app):
@app.get("/regular")
def regular_request(request):
return text(request.__class__.__name__)

_, response = await app.asgi_client.get("/regular")
assert response.body == b"Request"


@pytest.mark.asyncio
async def test_request_class_custom():
class MyCustomRequest(Request):
pass

app = Sanic(request_class=MyCustomRequest)

@app.get("/custom")
def custom_request(request):
return text(request.__class__.__name__)

_, response = await app.asgi_client.get("/custom")
assert response.body == b"MyCustomRequest"
14 changes: 7 additions & 7 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ def temp_path():


class ConfigTest:
not_for_config = 'should not be used'
CONFIG_VALUE = 'should be used'
not_for_config = "should not be used"
CONFIG_VALUE = "should be used"


def test_load_from_object(app):
Expand All @@ -31,15 +31,15 @@ def test_load_from_object(app):


def test_load_from_object_string(app):
app.config.from_object('test_config.ConfigTest')
assert 'CONFIG_VALUE' in app.config
assert app.config.CONFIG_VALUE == 'should be used'
assert 'not_for_config' not in app.config
app.config.from_object("test_config.ConfigTest")
assert "CONFIG_VALUE" in app.config
assert app.config.CONFIG_VALUE == "should be used"
assert "not_for_config" not in app.config


def test_load_from_object_string_exception(app):
with pytest.raises(ImportError):
app.config.from_object('test_config.Config.test')
app.config.from_object("test_config.Config.test")


def test_auto_load_env():
Expand Down
9 changes: 5 additions & 4 deletions tests/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import inspect

import pytest

from sanic import helpers
from sanic.config import Config
import pytest


def test_has_message_body():
Expand Down Expand Up @@ -63,15 +64,15 @@ def test_remove_entity_headers():


def test_import_string_class():
obj = helpers.import_string('sanic.config.Config')
obj = helpers.import_string("sanic.config.Config")
assert isinstance(obj, Config)


def test_import_string_module():
module = helpers.import_string('sanic.config')
module = helpers.import_string("sanic.config")
assert inspect.ismodule(module)


def test_import_string_exception():
with pytest.raises(ImportError):
helpers.import_string('test.test.test')
helpers.import_string("test.test.test")
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@ deps =
bandit

commands =
bandit --recursive sanic --skip B404,B101 --exclude sanic/reloader_helpers.py
bandit --recursive sanic --skip B404,B101 --exclude sanic/reloader_helpers.py

0 comments on commit 1eaa2e3

Please sign in to comment.