diff --git a/examples/run_async.py b/examples/run_async.py new file mode 100644 index 00000000..71912e5a --- /dev/null +++ b/examples/run_async.py @@ -0,0 +1,20 @@ +from sanic import Sanic +from sanic.response import json +from multiprocessing import Event +from signal import signal, SIGINT +import asyncio + +app = Sanic(__name__) + +@app.route("/") +async def test(request): + return json({"answer": "42"}) + +server = app.create_server(host="0.0.0.0", port=8001) +loop = asyncio.get_event_loop() +task = asyncio.ensure_future(server) +signal(SIGINT, lambda s, f: loop.close()) +try: + loop.run_forever() +except: + loop.stop() diff --git a/sanic/sanic.py b/sanic/sanic.py index cea09470..c56e2420 100644 --- a/sanic/sanic.py +++ b/sanic/sanic.py @@ -348,9 +348,8 @@ class Sanic: log.debug(self.config.LOGO) # Serve - if ssl is None: - proto = "http" - else: + proto = "http" + if ssl is not None: proto = "https" log.info('Goin\' Fast @ {}://{}:{}'.format(proto, host, port)) @@ -378,6 +377,63 @@ class Sanic: self.sock.close() get_event_loop().stop() + async def create_server(self, host="127.0.0.1", port=8000, debug=False, + before_start=None, after_start=None, + before_stop=None, after_stop=None, ssl=None, + sock=None, loop=None, protocol=HttpProtocol, + backlog=100, stop_event=None): + ''' + Asynchronous version of `run`. + ''' + loop = get_event_loop() + server_settings = { + 'protocol': protocol, + 'host': host, + 'port': port, + 'sock': sock, + 'ssl': ssl, + 'debug': debug, + 'request_handler': self.handle_request, + 'error_handler': self.error_handler, + 'request_timeout': self.config.REQUEST_TIMEOUT, + 'request_max_size': self.config.REQUEST_MAX_SIZE, + 'loop': loop, + 'backlog': backlog + } + + # -------------------------------------------- # + # Register start/stop events + # -------------------------------------------- # + + for event_name, settings_name, args, reverse in ( + ("before_server_start", "before_start", before_start, False), + ("after_server_start", "after_start", after_start, False), + ("before_server_stop", "before_stop", before_stop, True), + ("after_server_stop", "after_stop", after_stop, True), + ): + listeners = [] + for blueprint in self.blueprints.values(): + listeners += blueprint.listeners[event_name] + if args: + if callable(args): + args = [args] + listeners += args + if reverse: + listeners.reverse() + # Prepend sanic to the arguments when listeners are triggered + listeners = [partial(listener, self) for listener in listeners] + server_settings[settings_name] = listeners + + server_settings['run_async'] = True + + # Serve + proto = "http" + if ssl is not None: + proto = "https" + log.info('Goin\' Fast @ {}://{}:{}'.format(proto, host, port)) + + return await serve(**server_settings) + def serve_multiple(self, server_settings, workers, stop_event=None): """ Starts multiple server processes simultaneously. Stops on interrupt diff --git a/sanic/server.py b/sanic/server.py index 74ffb832..cf04df16 100644 --- a/sanic/server.py +++ b/sanic/server.py @@ -262,7 +262,7 @@ def serve(host, port, request_handler, error_handler, before_start=None, after_start=None, before_stop=None, after_stop=None, debug=False, request_timeout=60, ssl=None, sock=None, request_max_size=None, reuse_port=False, loop=None, protocol=HttpProtocol, backlog=100, - register_sys_signals=True): + register_sys_signals=True, run_async=False): """ Starts asynchronous HTTP Server on an individual process. @@ -320,11 +320,13 @@ def serve(host, port, request_handler, error_handler, before_start=None, sock=sock, backlog=backlog ) - # Instead of pulling time at the end of every request, # pull it once per minute loop.call_soon(partial(update_current_time, loop)) + if run_async: + return server_coroutine + try: http_server = loop.run_until_complete(server_coroutine) except Exception: