From 753d2da6dbd0deb6f8cb2ceab7c93c8fba56acac Mon Sep 17 00:00:00 2001 From: Raphael Deem Date: Sat, 28 Jan 2017 15:26:44 -0800 Subject: [PATCH 1/3] fix async run --- sanic/server.py | 4 ++-- tests/test_loop_policy.py | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 tests/test_loop_policy.py diff --git a/sanic/server.py b/sanic/server.py index 48c3827e..be9bc31c 100644 --- a/sanic/server.py +++ b/sanic/server.py @@ -297,8 +297,8 @@ def serve(host, port, request_handler, error_handler, before_start=None, :param protocol: Subclass of asyncio protocol class :return: Nothing """ - loop = async_loop.new_event_loop() - asyncio.set_event_loop(loop) + loop = asyncio.get_event_loop() + asyncio.set_event_loop_policy(async_loop.EventLoopPolicy()) if debug: loop.set_debug(debug) diff --git a/tests/test_loop_policy.py b/tests/test_loop_policy.py new file mode 100644 index 00000000..f764548c --- /dev/null +++ b/tests/test_loop_policy.py @@ -0,0 +1,27 @@ +from sanic import Sanic +import asyncio +from signal import signal, SIGINT +import uvloop + + +def test_loop_policy(): + app = Sanic('test_loop_policy') + + server = app.create_server(host="0.0.0.0", port=8000) + + loop = asyncio.get_event_loop() + task = asyncio.ensure_future(server) + signal(SIGINT, lambda s, f: loop.close()) + + # serve() sets the event loop policy to uvloop but + # doesn't get called until we run the server task + assert isinstance(asyncio.get_event_loop_policy(), + asyncio.unix_events._UnixDefaultEventLoopPolicy) + + try: + loop.run_until_complete(task) + except: + loop.stop() + + assert isinstance(asyncio.get_event_loop_policy(), + uvloop.EventLoopPolicy) From 10dbb9186d3ba0f23728f5974bc12262be67d7c3 Mon Sep 17 00:00:00 2001 From: Raphael Deem Date: Sat, 28 Jan 2017 14:54:15 -0800 Subject: [PATCH 2/3] combine logic from create_server() and run() --- sanic/sanic.py | 159 +++++++++++++++++++------------------- sanic/server.py | 4 +- tests/test_loop_policy.py | 27 ------- 3 files changed, 81 insertions(+), 109 deletions(-) delete mode 100644 tests/test_loop_policy.py diff --git a/sanic/sanic.py b/sanic/sanic.py index ea2e8bef..e468ad7c 100644 --- a/sanic/sanic.py +++ b/sanic/sanic.py @@ -298,6 +298,84 @@ class Sanic: :param protocol: Subclass of asyncio protocol class :return: Nothing """ + server_settings = \ + self._helper(host=host, port=port, debug=debug, + before_start=before_start, after_start=after_start, + before_stop=before_stop, after_stop=after_stop, + ssl=ssl, sock=sock, workers=workers, loop=loop, + protocol=protocol, backlog=backlog, + stop_event=stop_event, + register_sys_signals=register_sys_signals) + try: + if workers == 1: + serve(**server_settings) + else: + serve_multiple(server_settings, workers, stop_event) + + except Exception as e: + log.exception( + 'Experienced exception while trying to serve') + + log.info("Server Stopped") + + def stop(self): + """This kills the Sanic""" + 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`. + """ + server_settings = \ + self._helper(host=host, port=port, debug=debug, + before_start=before_start, after_start=after_start, + before_stop=before_stop, after_stop=after_stop, + ssl=ssl, sock=sock, loop=loop, + protocol=protocol, backlog=backlog, + stop_event=stop_event) + + 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 _helper(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, workers=1, loop=None, + protocol=HttpProtocol, backlog=100, stop_event=None, + register_sys_signals=True): + """ + Runs the HTTP Server and listens until keyboard interrupt or term + signal. On termination, drains connections before closing. + + :param host: Address to host on + :param port: Port to host on + :param debug: Enables debug output (slows server) + :param before_start: Functions to be executed before the server starts + accepting connections + :param after_start: Functions to be executed after the server starts + accepting connections + :param before_stop: Functions to be executed when a stop signal is + received before it is respected + :param after_stop: Functions to be executed when all requests are + complete + :param ssl: SSLContext for SSL encryption of worker(s) + :param sock: Socket for the server to accept connections from + :param workers: Number of processes + received before it is respected + :param protocol: Subclass of asyncio protocol class + :return: Nothing + """ + self.error_handler.debug = debug self.debug = debug self.loop = loop @@ -357,83 +435,4 @@ class Sanic: if ssl is not None: proto = "https" log.info('Goin\' Fast @ {}://{}:{}'.format(proto, host, port)) - - try: - if workers == 1: - serve(**server_settings) - else: - serve_multiple(server_settings, workers, stop_event) - - except Exception as e: - log.exception( - 'Experienced exception while trying to serve') - - log.info("Server Stopped") - - def stop(self): - """This kills the Sanic""" - 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`. - """ - if loop is not None: - if self.debug: - warnings.simplefilter('default') - warnings.warn("Passing a loop will be deprecated in version" - " 0.4.0 https://github.com/channelcat/sanic/" - "pull/335 has more information.", - DeprecationWarning) - - 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) + return server_settings diff --git a/sanic/server.py b/sanic/server.py index be9bc31c..48c3827e 100644 --- a/sanic/server.py +++ b/sanic/server.py @@ -297,8 +297,8 @@ def serve(host, port, request_handler, error_handler, before_start=None, :param protocol: Subclass of asyncio protocol class :return: Nothing """ - loop = asyncio.get_event_loop() - asyncio.set_event_loop_policy(async_loop.EventLoopPolicy()) + loop = async_loop.new_event_loop() + asyncio.set_event_loop(loop) if debug: loop.set_debug(debug) diff --git a/tests/test_loop_policy.py b/tests/test_loop_policy.py deleted file mode 100644 index f764548c..00000000 --- a/tests/test_loop_policy.py +++ /dev/null @@ -1,27 +0,0 @@ -from sanic import Sanic -import asyncio -from signal import signal, SIGINT -import uvloop - - -def test_loop_policy(): - app = Sanic('test_loop_policy') - - server = app.create_server(host="0.0.0.0", port=8000) - - loop = asyncio.get_event_loop() - task = asyncio.ensure_future(server) - signal(SIGINT, lambda s, f: loop.close()) - - # serve() sets the event loop policy to uvloop but - # doesn't get called until we run the server task - assert isinstance(asyncio.get_event_loop_policy(), - asyncio.unix_events._UnixDefaultEventLoopPolicy) - - try: - loop.run_until_complete(task) - except: - loop.stop() - - assert isinstance(asyncio.get_event_loop_policy(), - uvloop.EventLoopPolicy) From 82d1d30a41ea61f23884bdc862dd6b090ff0bda4 Mon Sep 17 00:00:00 2001 From: Raphael Deem Date: Sun, 29 Jan 2017 14:01:00 -0800 Subject: [PATCH 3/3] review updates --- sanic/sanic.py | 59 +++++++++++++++++--------------------------------- 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/sanic/sanic.py b/sanic/sanic.py index e468ad7c..a81a336a 100644 --- a/sanic/sanic.py +++ b/sanic/sanic.py @@ -298,14 +298,12 @@ class Sanic: :param protocol: Subclass of asyncio protocol class :return: Nothing """ - server_settings = \ - self._helper(host=host, port=port, debug=debug, - before_start=before_start, after_start=after_start, - before_stop=before_stop, after_stop=after_stop, - ssl=ssl, sock=sock, workers=workers, loop=loop, - protocol=protocol, backlog=backlog, - stop_event=stop_event, - register_sys_signals=register_sys_signals) + server_settings = self._helper( + host=host, port=port, debug=debug, before_start=before_start, + after_start=after_start, before_stop=before_stop, + after_stop=after_stop, ssl=ssl, sock=sock, workers=workers, + loop=loop, protocol=protocol, backlog=backlog, + stop_event=stop_event, register_sys_signals=register_sys_signals) try: if workers == 1: serve(**server_settings) @@ -330,15 +328,12 @@ class Sanic: """ Asynchronous version of `run`. """ - server_settings = \ - self._helper(host=host, port=port, debug=debug, - before_start=before_start, after_start=after_start, - before_stop=before_stop, after_stop=after_stop, - ssl=ssl, sock=sock, loop=loop, - protocol=protocol, backlog=backlog, - stop_event=stop_event) - - server_settings['run_async'] = True + server_settings = self._helper( + host=host, port=port, debug=debug, before_start=before_start, + after_start=after_start, before_stop=before_stop, + after_stop=after_stop, ssl=ssl, sock=sock, loop=loop, + protocol=protocol, backlog=backlog, stop_event=stop_event, + async_run=True) # Serve proto = "http" @@ -352,33 +347,14 @@ class Sanic: before_start=None, after_start=None, before_stop=None, after_stop=None, ssl=None, sock=None, workers=1, loop=None, protocol=HttpProtocol, backlog=100, stop_event=None, - register_sys_signals=True): + register_sys_signals=True, run_async=False): """ - Runs the HTTP Server and listens until keyboard interrupt or term - signal. On termination, drains connections before closing. - - :param host: Address to host on - :param port: Port to host on - :param debug: Enables debug output (slows server) - :param before_start: Functions to be executed before the server starts - accepting connections - :param after_start: Functions to be executed after the server starts - accepting connections - :param before_stop: Functions to be executed when a stop signal is - received before it is respected - :param after_stop: Functions to be executed when all requests are - complete - :param ssl: SSLContext for SSL encryption of worker(s) - :param sock: Socket for the server to accept connections from - :param workers: Number of processes - received before it is respected - :param protocol: Subclass of asyncio protocol class - :return: Nothing + Helper function used by `run` and `create_server`. """ self.error_handler.debug = debug self.debug = debug - self.loop = loop + self.loop = loop = get_event_loop() if loop is not None: if self.debug: @@ -399,6 +375,7 @@ class Sanic: 'error_handler': self.error_handler, 'request_timeout': self.config.REQUEST_TIMEOUT, 'request_max_size': self.config.REQUEST_MAX_SIZE, + 'loop': loop, 'register_sys_signals': register_sys_signals, 'backlog': backlog } @@ -430,9 +407,13 @@ class Sanic: log.setLevel(logging.DEBUG) log.debug(self.config.LOGO) + if run_async: + server_settings['run_async'] = True + # Serve proto = "http" if ssl is not None: proto = "https" log.info('Goin\' Fast @ {}://{}:{}'.format(proto, host, port)) + return server_settings