Merge pull request #1470 from denismakogon/create-server

make Sanic.create_server return an asyncio.Server
This commit is contained in:
7 2019-01-20 14:21:11 -08:00 committed by GitHub
commit 9cf2e1b519
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 132 additions and 11 deletions

View File

@ -33,6 +33,7 @@ Guides
sanic/changelog
sanic/contributing
sanic/api_reference
sanic/asyncio_python37
Module Documentation

View File

@ -0,0 +1,58 @@
Python 3.7 AsyncIO examples
###########################
With Python 3.7 AsyncIO got major update for the following types:
- asyncio.AbstractEventLoop
- asyncio.AnstractServer
This example shows how to use sanic with Python 3.7, to be precise: how to retrieve an asyncio server instance:
.. code:: python
import asyncio
import socket
import os
from sanic import Sanic
from sanic.response import json
app = Sanic(__name__)
@app.route("/")
async def test(request):
return json({"hello": "world"})
server_socket = '/tmp/sanic.sock'
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
os.remote(server_socket)
finally:
sock.bind(server_socket)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
srv_coro = app.create_server(
sock=sock,
return_asyncio_server=True,
asyncio_server_args=dict(
start_serving=False
)
)
srv = loop.run_until_complete(srv_coro)
try:
assert srv.is_serving() is False
loop.run_until_complete(srv.start_serving())
assert srv.is_serving() is True
loop.run_until_complete(srv.serve_forever())
except KeyboardInterrupt:
srv.close()
loop.close()
Please note that uvloop does not support these features yet.

View File

@ -1121,6 +1121,8 @@ class Sanic:
backlog: int = 100,
stop_event: Any = None,
access_log: Optional[bool] = None,
return_asyncio_server=False,
asyncio_server_kwargs=None,
) -> None:
"""
Asynchronous version of :func:`run`.
@ -1154,6 +1156,13 @@ class Sanic:
:type stop_event: None
:param access_log: Enables writing access logs (slows server)
:type access_log: bool
:param return_asyncio_server: flag that defines whether there's a need
to return asyncio.Server or
start it serving right away
:type return_asyncio_server: bool
:param asyncio_server_kwargs: key-value arguments for
asyncio/uvloop create_server method
:type asyncio_server_kwargs: dict
:return: Nothing
"""
@ -1184,7 +1193,7 @@ class Sanic:
loop=get_event_loop(),
protocol=protocol,
backlog=backlog,
run_async=True,
run_async=return_asyncio_server,
)
# Trigger before_start events
@ -1193,7 +1202,9 @@ class Sanic:
server_settings.get("loop"),
)
return await serve(**server_settings)
return await serve(
asyncio_server_kwargs=asyncio_server_kwargs, **server_settings
)
async def trigger_events(self, events, loop):
"""Trigger events (functions or async)

View File

@ -656,6 +656,7 @@ def serve(
websocket_write_limit=2 ** 16,
state=None,
graceful_shutdown_timeout=15.0,
asyncio_server_kwargs=None,
):
"""Start asynchronous HTTP Server on an individual process.
@ -700,6 +701,8 @@ def serve(
:param router: Router object
:param graceful_shutdown_timeout: How long take to Force close non-idle
connection
:param asyncio_server_kwargs: key-value args for asyncio/uvloop
create_server method
:return: Nothing
"""
if not run_async:
@ -734,7 +737,9 @@ def serve(
state=state,
debug=debug,
)
asyncio_server_kwargs = (
asyncio_server_kwargs if asyncio_server_kwargs else {}
)
server_coroutine = loop.create_server(
server,
host,
@ -743,6 +748,7 @@ def serve(
reuse_port=reuse_port,
sock=sock,
backlog=backlog,
**asyncio_server_kwargs
)
# Instead of pulling time at the end of every request,

View File

@ -1,12 +1,22 @@
import asyncio
import logging
import sys
from inspect import isawaitable
import pytest
from sanic.exceptions import SanicException
from sanic.response import text
def uvloop_installed():
try:
import uvloop
return True
except ImportError:
return False
def test_app_loop_running(app):
@app.get("/test")
async def handler(request):
@ -17,6 +27,32 @@ def test_app_loop_running(app):
assert response.text == "pass"
@pytest.mark.skipif(sys.version_info < (3, 7),
reason="requires python3.7 or higher")
def test_create_asyncio_server(app):
if not uvloop_installed():
loop = asyncio.get_event_loop()
asyncio_srv_coro = app.create_server(
return_asyncio_server=True)
assert isawaitable(asyncio_srv_coro)
srv = loop.run_until_complete(asyncio_srv_coro)
assert srv.is_serving() is True
@pytest.mark.skipif(sys.version_info < (3, 7),
reason="requires python3.7 or higher")
def test_asyncio_server_start_serving(app):
if not uvloop_installed():
loop = asyncio.get_event_loop()
asyncio_srv_coro = app.create_server(
return_asyncio_server=True,
asyncio_server_kwargs=dict(
start_serving=False
))
srv = loop.run_until_complete(asyncio_srv_coro)
assert srv.is_serving() is False
def test_app_loop_not_running(app):
with pytest.raises(SanicException) as excinfo:
app.loop

View File

@ -231,10 +231,12 @@ async def test_config_access_log_passing_in_create_server(app):
async def _request(sanic, loop):
app.stop()
await app.create_server(port=1341, access_log=False)
await app.create_server(port=1341, access_log=False,
return_asyncio_server=True)
assert app.config.ACCESS_LOG == False
await app.create_server(port=1342, access_log=True)
await app.create_server(port=1342, access_log=True,
return_asyncio_server=True)
assert app.config.ACCESS_LOG == True

View File

@ -51,7 +51,9 @@ class ReuseableSanicTestClient(SanicTestClient):
uri="/",
gather_request=True,
debug=False,
server_kwargs={},
server_kwargs={
"return_asyncio_server": True,
},
*request_args,
**request_kwargs
):

View File

@ -12,7 +12,8 @@ except BaseException:
def test_logo_base(app, caplog):
server = app.create_server(debug=True)
server = app.create_server(
debug=True, return_asyncio_server=True)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop._stopping = False
@ -31,7 +32,8 @@ def test_logo_base(app, caplog):
def test_logo_false(app, caplog):
app.config.LOGO = False
server = app.create_server(debug=True)
server = app.create_server(
debug=True, return_asyncio_server=True)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop._stopping = False
@ -50,7 +52,8 @@ def test_logo_false(app, caplog):
def test_logo_true(app, caplog):
app.config.LOGO = True
server = app.create_server(debug=True)
server = app.create_server(
debug=True, return_asyncio_server=True)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop._stopping = False
@ -69,7 +72,8 @@ def test_logo_true(app, caplog):
def test_logo_custom(app, caplog):
app.config.LOGO = "My Custom Logo"
server = app.create_server(debug=True)
server = app.create_server(
debug=True, return_asyncio_server=True)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop._stopping = False

View File

@ -83,7 +83,8 @@ async def test_trigger_before_events_create_server(app):
async def init_db(app, loop):
app.db = MySanicDb()
await app.create_server()
await app.create_server(
debug=True, return_asyncio_server=True)
assert hasattr(app, "db")
assert isinstance(app.db, MySanicDb)