Merge pull request #1470 from denismakogon/create-server
make Sanic.create_server return an asyncio.Server
This commit is contained in:
commit
9cf2e1b519
|
@ -33,6 +33,7 @@ Guides
|
||||||
sanic/changelog
|
sanic/changelog
|
||||||
sanic/contributing
|
sanic/contributing
|
||||||
sanic/api_reference
|
sanic/api_reference
|
||||||
|
sanic/asyncio_python37
|
||||||
|
|
||||||
|
|
||||||
Module Documentation
|
Module Documentation
|
||||||
|
|
58
docs/sanic/asyncio_python37.rst
Normal file
58
docs/sanic/asyncio_python37.rst
Normal 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.
|
15
sanic/app.py
15
sanic/app.py
|
@ -1121,6 +1121,8 @@ class Sanic:
|
||||||
backlog: int = 100,
|
backlog: int = 100,
|
||||||
stop_event: Any = None,
|
stop_event: Any = None,
|
||||||
access_log: Optional[bool] = None,
|
access_log: Optional[bool] = None,
|
||||||
|
return_asyncio_server=False,
|
||||||
|
asyncio_server_kwargs=None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Asynchronous version of :func:`run`.
|
Asynchronous version of :func:`run`.
|
||||||
|
@ -1154,6 +1156,13 @@ class Sanic:
|
||||||
:type stop_event: None
|
:type stop_event: None
|
||||||
:param access_log: Enables writing access logs (slows server)
|
:param access_log: Enables writing access logs (slows server)
|
||||||
:type access_log: bool
|
: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
|
:return: Nothing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -1184,7 +1193,7 @@ class Sanic:
|
||||||
loop=get_event_loop(),
|
loop=get_event_loop(),
|
||||||
protocol=protocol,
|
protocol=protocol,
|
||||||
backlog=backlog,
|
backlog=backlog,
|
||||||
run_async=True,
|
run_async=return_asyncio_server,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Trigger before_start events
|
# Trigger before_start events
|
||||||
|
@ -1193,7 +1202,9 @@ class Sanic:
|
||||||
server_settings.get("loop"),
|
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):
|
async def trigger_events(self, events, loop):
|
||||||
"""Trigger events (functions or async)
|
"""Trigger events (functions or async)
|
||||||
|
|
|
@ -656,6 +656,7 @@ def serve(
|
||||||
websocket_write_limit=2 ** 16,
|
websocket_write_limit=2 ** 16,
|
||||||
state=None,
|
state=None,
|
||||||
graceful_shutdown_timeout=15.0,
|
graceful_shutdown_timeout=15.0,
|
||||||
|
asyncio_server_kwargs=None,
|
||||||
):
|
):
|
||||||
"""Start asynchronous HTTP Server on an individual process.
|
"""Start asynchronous HTTP Server on an individual process.
|
||||||
|
|
||||||
|
@ -700,6 +701,8 @@ def serve(
|
||||||
:param router: Router object
|
:param router: Router object
|
||||||
:param graceful_shutdown_timeout: How long take to Force close non-idle
|
:param graceful_shutdown_timeout: How long take to Force close non-idle
|
||||||
connection
|
connection
|
||||||
|
:param asyncio_server_kwargs: key-value args for asyncio/uvloop
|
||||||
|
create_server method
|
||||||
:return: Nothing
|
:return: Nothing
|
||||||
"""
|
"""
|
||||||
if not run_async:
|
if not run_async:
|
||||||
|
@ -734,7 +737,9 @@ def serve(
|
||||||
state=state,
|
state=state,
|
||||||
debug=debug,
|
debug=debug,
|
||||||
)
|
)
|
||||||
|
asyncio_server_kwargs = (
|
||||||
|
asyncio_server_kwargs if asyncio_server_kwargs else {}
|
||||||
|
)
|
||||||
server_coroutine = loop.create_server(
|
server_coroutine = loop.create_server(
|
||||||
server,
|
server,
|
||||||
host,
|
host,
|
||||||
|
@ -743,6 +748,7 @@ def serve(
|
||||||
reuse_port=reuse_port,
|
reuse_port=reuse_port,
|
||||||
sock=sock,
|
sock=sock,
|
||||||
backlog=backlog,
|
backlog=backlog,
|
||||||
|
**asyncio_server_kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
# Instead of pulling time at the end of every request,
|
# Instead of pulling time at the end of every request,
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from inspect import isawaitable
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic.exceptions import SanicException
|
from sanic.exceptions import SanicException
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
|
def uvloop_installed():
|
||||||
|
try:
|
||||||
|
import uvloop
|
||||||
|
return True
|
||||||
|
except ImportError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def test_app_loop_running(app):
|
def test_app_loop_running(app):
|
||||||
@app.get("/test")
|
@app.get("/test")
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
|
@ -17,6 +27,32 @@ def test_app_loop_running(app):
|
||||||
assert response.text == "pass"
|
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):
|
def test_app_loop_not_running(app):
|
||||||
with pytest.raises(SanicException) as excinfo:
|
with pytest.raises(SanicException) as excinfo:
|
||||||
app.loop
|
app.loop
|
||||||
|
|
|
@ -231,10 +231,12 @@ async def test_config_access_log_passing_in_create_server(app):
|
||||||
async def _request(sanic, loop):
|
async def _request(sanic, loop):
|
||||||
app.stop()
|
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
|
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
|
assert app.config.ACCESS_LOG == True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,9 @@ class ReuseableSanicTestClient(SanicTestClient):
|
||||||
uri="/",
|
uri="/",
|
||||||
gather_request=True,
|
gather_request=True,
|
||||||
debug=False,
|
debug=False,
|
||||||
server_kwargs={},
|
server_kwargs={
|
||||||
|
"return_asyncio_server": True,
|
||||||
|
},
|
||||||
*request_args,
|
*request_args,
|
||||||
**request_kwargs
|
**request_kwargs
|
||||||
):
|
):
|
||||||
|
|
|
@ -12,7 +12,8 @@ except BaseException:
|
||||||
|
|
||||||
|
|
||||||
def test_logo_base(app, caplog):
|
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()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
loop._stopping = False
|
loop._stopping = False
|
||||||
|
@ -31,7 +32,8 @@ def test_logo_base(app, caplog):
|
||||||
def test_logo_false(app, caplog):
|
def test_logo_false(app, caplog):
|
||||||
app.config.LOGO = False
|
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()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
loop._stopping = False
|
loop._stopping = False
|
||||||
|
@ -50,7 +52,8 @@ def test_logo_false(app, caplog):
|
||||||
def test_logo_true(app, caplog):
|
def test_logo_true(app, caplog):
|
||||||
app.config.LOGO = True
|
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()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
loop._stopping = False
|
loop._stopping = False
|
||||||
|
@ -69,7 +72,8 @@ def test_logo_true(app, caplog):
|
||||||
def test_logo_custom(app, caplog):
|
def test_logo_custom(app, caplog):
|
||||||
app.config.LOGO = "My Custom Logo"
|
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()
|
loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
loop._stopping = False
|
loop._stopping = False
|
||||||
|
|
|
@ -83,7 +83,8 @@ async def test_trigger_before_events_create_server(app):
|
||||||
async def init_db(app, loop):
|
async def init_db(app, loop):
|
||||||
app.db = MySanicDb()
|
app.db = MySanicDb()
|
||||||
|
|
||||||
await app.create_server()
|
await app.create_server(
|
||||||
|
debug=True, return_asyncio_server=True)
|
||||||
|
|
||||||
assert hasattr(app, "db")
|
assert hasattr(app, "db")
|
||||||
assert isinstance(app.db, MySanicDb)
|
assert isinstance(app.db, MySanicDb)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user