Merge pull request #408 from agoose77/master

Use app decorator instead of run arguments for before_start
This commit is contained in:
Eli Uriegas 2017-02-13 12:54:46 -06:00 committed by GitHub
commit 1660041470
13 changed files with 91 additions and 61 deletions

View File

@ -7,15 +7,6 @@ keyword arguments:
- `host` *(default `"127.0.0.1"`)*: Address to host the server on. - `host` *(default `"127.0.0.1"`)*: Address to host the server on.
- `port` *(default `8000`)*: Port to host the server on. - `port` *(default `8000`)*: Port to host the server on.
- `debug` *(default `False`)*: Enables debug output (slows server). - `debug` *(default `False`)*: Enables debug output (slows server).
- `before_start` *(default `None`)*: Function or list of functions to be executed
before the server starts accepting connections.
- `after_start` *(default `None`)*: Function or list of functions to be executed
after the server starts accepting connections.
- `before_stop` *(default `None`)*: Function or list of functions to be
executed when a stop signal is received before it is
respected.
- `after_stop` *(default `None`)*: Function or list of functions to be executed
when all requests are complete.
- `ssl` *(default `None`)*: `SSLContext` for SSL encryption of worker(s). - `ssl` *(default `None`)*: `SSLContext` for SSL encryption of worker(s).
- `sock` *(default `None`)*: Socket for the server to accept connections from. - `sock` *(default `None`)*: Socket for the server to accept connections from.
- `workers` *(default `1`)*: Number of worker processes to spawn. - `workers` *(default `1`)*: Number of worker processes to spawn.

View File

@ -8,6 +8,7 @@ app = Sanic(__name__)
sem = None sem = None
@app.listener('before_server_start')
def init(sanic, loop): def init(sanic, loop):
global sem global sem
CONCURRENCY_PER_WORKER = 4 CONCURRENCY_PER_WORKER = 4
@ -33,4 +34,4 @@ async def test(request):
return json(response) return json(response)
app.run(host="0.0.0.0", port=8000, workers=2, before_start=init) app.run(host="0.0.0.0", port=8000, workers=2)

View File

@ -26,6 +26,7 @@ async def get_pool():
app = Sanic(name=__name__) app = Sanic(name=__name__)
@app.listener('before_server_start')
async def prepare_db(app, loop): async def prepare_db(app, loop):
""" """
Let's create some table and add some data Let's create some table and add some data
@ -61,5 +62,4 @@ async def handle(request):
if __name__ == '__main__': if __name__ == '__main__':
app.run(host='0.0.0.0', app.run(host='0.0.0.0',
port=8000, port=8000,
debug=True, debug=True)
before_start=prepare_db)

View File

@ -32,7 +32,7 @@ polls = sa.Table('sanic_polls', metadata,
app = Sanic(name=__name__) app = Sanic(name=__name__)
@app.listener('before_server_start')
async def prepare_db(app, loop): async def prepare_db(app, loop):
""" Let's add some data """ Let's add some data
@ -58,9 +58,10 @@ async def handle(request):
async with engine.acquire() as conn: async with engine.acquire() as conn:
result = [] result = []
async for row in conn.execute(polls.select()): async for row in conn.execute(polls.select()):
result.append({"question": row.question, "pub_date": row.pub_date}) result.append({"question": row.question,
"pub_date": row.pub_date})
return json({"polls": result}) return json({"polls": result})
if __name__ == '__main__': if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, before_start=prepare_db) app.run(host='0.0.0.0', port=8000)

View File

@ -27,6 +27,7 @@ def jsonify(records):
app = Sanic(__name__) app = Sanic(__name__)
@app.listener('before_server_start')
async def create_db(app, loop): async def create_db(app, loop):
""" """
Create some table and add some data Create some table and add some data
@ -55,4 +56,4 @@ async def handler(request):
if __name__ == '__main__': if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, before_start=create_db) app.run(host='0.0.0.0', port=8000)

View File

@ -14,14 +14,6 @@ from peewee_async import Manager, PostgresqlDatabase
# we instantiate a custom loop so we can pass it to our db manager # we instantiate a custom loop so we can pass it to our db manager
def setup(app, loop):
database = PostgresqlDatabase(database='test',
host='127.0.0.1',
user='postgres',
password='mysecretpassword')
objects = Manager(database, loop=loop)
## from peewee_async docs: ## from peewee_async docs:
# Also theres no need to connect and re-connect before executing async queries # Also theres no need to connect and re-connect before executing async queries
# with manager! Its all automatic. But you can run Manager.connect() or # with manager! Its all automatic. But you can run Manager.connect() or
@ -48,6 +40,15 @@ objects.database.allow_sync = False # this will raise AssertionError on ANY sync
app = Sanic('peewee_example') app = Sanic('peewee_example')
@app.listener('before_server_start')
def setup(app, loop):
database = PostgresqlDatabase(database='test',
host='127.0.0.1',
user='postgres',
password='mysecretpassword')
objects = Manager(database, loop=loop)
@app.route('/post/<key>/<value>') @app.route('/post/<key>/<value>')
async def post(request, key, value): async def post(request, key, value):
""" """
@ -75,4 +76,4 @@ async def get(request):
if __name__ == "__main__": if __name__ == "__main__":
app.run(host='0.0.0.0', port=8000, before_start=setup) app.run(host='0.0.0.0', port=8000)

View File

@ -64,12 +64,14 @@ def query_string(request):
# Run Server # Run Server
# ----------------------------------------------- # # ----------------------------------------------- #
@app.listener('after_server_start')
def after_start(app, loop): def after_start(app, loop):
log.info("OH OH OH OH OHHHHHHHH") log.info("OH OH OH OH OHHHHHHHH")
@app.listener('before_server_stop')
def before_stop(app, loop): def before_stop(app, loop):
log.info("TRIED EVERYTHING") log.info("TRIED EVERYTHING")
app.run(host="0.0.0.0", port=8000, debug=True, after_start=after_start, before_stop=before_stop) app.run(host="0.0.0.0", port=8000, debug=True)

View File

@ -65,6 +65,11 @@ class Blueprint:
app.static(uri, future.file_or_directory, app.static(uri, future.file_or_directory,
*future.args, **future.kwargs) *future.args, **future.kwargs)
# Event listeners
for event, listeners in self.listeners.items():
for listener in listeners:
app.listener(event)(listener)
def route(self, uri, methods=frozenset({'GET'}), host=None): def route(self, uri, methods=frozenset({'GET'}), host=None):
""" """
Creates a blueprint route from a decorated function. Creates a blueprint route from a decorated function.

View File

@ -2,7 +2,7 @@ import logging
import re import re
import warnings import warnings
from asyncio import get_event_loop from asyncio import get_event_loop
from collections import deque from collections import deque, defaultdict
from functools import partial from functools import partial
from inspect import isawaitable, stack, getmodulename from inspect import isawaitable, stack, getmodulename
from traceback import format_exc from traceback import format_exc
@ -10,8 +10,8 @@ from urllib.parse import urlencode, urlunparse
from .config import Config from .config import Config
from .constants import HTTP_METHODS from .constants import HTTP_METHODS
from .handlers import ErrorHandler
from .exceptions import ServerError, URLBuildError from .exceptions import ServerError, URLBuildError
from .handlers import ErrorHandler
from .log import log from .log import log
from .response import HTTPResponse from .response import HTTPResponse
from .router import Router from .router import Router
@ -21,6 +21,7 @@ from .views import CompositionView
class Sanic: class Sanic:
def __init__(self, name=None, router=None, def __init__(self, name=None, router=None,
error_handler=None): error_handler=None):
# Only set up a default log handler if the # Only set up a default log handler if the
@ -46,6 +47,7 @@ class Sanic:
self.debug = None self.debug = None
self.sock = None self.sock = None
self.processes = None self.processes = None
self.listeners = defaultdict(list)
# Register alternative method names # Register alternative method names
self.go_fast = self.run self.go_fast = self.run
@ -61,6 +63,18 @@ class Sanic:
# Registration # Registration
# -------------------------------------------------------------------- # # -------------------------------------------------------------------- #
# Decorator
def listener(self, event):
"""
Create a listener from a decorated function.
:param event: Event to listen to.
"""
def decorator(listener):
self.listeners[event].append(listener)
return listener
return decorator
# Decorator # Decorator
def route(self, uri, methods=frozenset({'GET'}), host=None): def route(self, uri, methods=frozenset({'GET'}), host=None):
""" """
@ -419,6 +433,7 @@ class Sanic:
after_stop=after_stop, ssl=ssl, sock=sock, workers=workers, after_stop=after_stop, ssl=ssl, sock=sock, workers=workers,
loop=loop, protocol=protocol, backlog=backlog, loop=loop, protocol=protocol, backlog=backlog,
stop_event=stop_event, register_sys_signals=register_sys_signals) stop_event=stop_event, register_sys_signals=register_sys_signals)
try: try:
if workers == 1: if workers == 1:
serve(**server_settings) serve(**server_settings)
@ -473,6 +488,16 @@ class Sanic:
"pull/335 has more information.", "pull/335 has more information.",
DeprecationWarning) DeprecationWarning)
# Deprecate this
if any(arg is not None for arg in (after_stop, after_start,
before_start, before_stop)):
if debug:
warnings.simplefilter('default')
warnings.warn("Passing a before_start, before_stop, after_start or"
"after_stop callback will be deprecated in next "
"major version after 0.4.0",
DeprecationWarning)
self.error_handler.debug = debug self.error_handler.debug = debug
self.debug = debug self.debug = debug
loop = self.loop loop = self.loop
@ -497,19 +522,18 @@ class Sanic:
# Register start/stop events # Register start/stop events
# -------------------------------------------- # # -------------------------------------------- #
for event_name, settings_name, args, reverse in ( for event_name, settings_name, reverse, args in (
("before_server_start", "before_start", before_start, False), ("before_server_start", "before_start", False, before_start),
("after_server_start", "after_start", after_start, False), ("after_server_start", "after_start", False, after_start),
("before_server_stop", "before_stop", before_stop, True), ("before_server_stop", "before_stop", True, before_stop),
("after_server_stop", "after_stop", after_stop, True), ("after_server_stop", "after_stop", True, after_stop),
): ):
listeners = [] listeners = self.listeners[event_name].copy()
for blueprint in self.blueprints.values():
listeners += blueprint.listeners[event_name]
if args: if args:
if callable(args): if callable(args):
args = [args] listeners.append(args)
listeners += args else:
listeners.extend(args)
if reverse: if reverse:
listeners.reverse() listeners.reverse()
# Prepend sanic to the arguments when listeners are triggered # Prepend sanic to the arguments when listeners are triggered

View File

@ -28,6 +28,7 @@ def sanic_endpoint_test(app, method='get', uri='/', gather_request=True,
results[0] = request results[0] = request
app.request_middleware.appendleft(_collect_request) app.request_middleware.appendleft(_collect_request)
@app.listener('after_server_start')
async def _collect_response(sanic, loop): async def _collect_response(sanic, loop):
try: try:
response = await local_request(method, uri, *request_args, response = await local_request(method, uri, *request_args,
@ -37,8 +38,8 @@ def sanic_endpoint_test(app, method='get', uri='/', gather_request=True,
exceptions.append(e) exceptions.append(e)
app.stop() app.stop()
app.run(host=HOST, debug=debug, port=PORT, app.run(host=HOST, debug=debug, port=PORT, **server_kwargs)
after_start=_collect_response, **server_kwargs) app.listeners['after_server_start'].pop()
if exceptions: if exceptions:
raise ValueError("Exception during request: {}".format(exceptions)) raise ValueError("Exception during request: {}".format(exceptions))

View File

@ -5,6 +5,7 @@ from sanic import Sanic
def test_bad_request_response(): def test_bad_request_response():
app = Sanic('test_bad_request_response') app = Sanic('test_bad_request_response')
lines = [] lines = []
@app.listener('after_server_start')
async def _request(sanic, loop): async def _request(sanic, loop):
connect = asyncio.open_connection('127.0.0.1', 42101) connect = asyncio.open_connection('127.0.0.1', 42101)
reader, writer = await connect reader, writer = await connect
@ -15,6 +16,6 @@ def test_bad_request_response():
break break
lines.append(line) lines.append(line)
app.stop() app.stop()
app.run(host='127.0.0.1', port=42101, debug=False, after_start=_request) app.run(host='127.0.0.1', port=42101, debug=False)
assert lines[0] == b'HTTP/1.1 400 Bad Request\r\n' assert lines[0] == b'HTTP/1.1 400 Bad Request\r\n'
assert lines[-1] == b'Error: Bad Request' assert lines[-1] == b'Error: Bad Request'

View File

@ -9,10 +9,10 @@ from sanic import Sanic
from sanic.utils import HOST, PORT from sanic.utils import HOST, PORT
AVAILABLE_LISTENERS = [ AVAILABLE_LISTENERS = [
'before_start', 'before_server_start',
'after_start', 'after_server_start',
'before_stop', 'before_server_stop',
'after_stop' 'after_server_stop'
] ]
@ -42,9 +42,10 @@ def test_single_listener(listener_name):
random_name_app = Sanic(''.join( random_name_app = Sanic(''.join(
[choice(ascii_letters) for _ in range(choice(range(5, 10)))])) [choice(ascii_letters) for _ in range(choice(range(5, 10)))]))
output = list() output = list()
start_stop_app( # Register listener
random_name_app, random_name_app.listener(listener_name)(
**{listener_name: create_listener(listener_name, output)}) create_listener(listener_name, output))
start_stop_app(random_name_app)
assert random_name_app.name + listener_name == output.pop() assert random_name_app.name + listener_name == output.pop()
@ -52,9 +53,9 @@ def test_all_listeners():
random_name_app = Sanic(''.join( random_name_app = Sanic(''.join(
[choice(ascii_letters) for _ in range(choice(range(5, 10)))])) [choice(ascii_letters) for _ in range(choice(range(5, 10)))]))
output = list() output = list()
start_stop_app( for listener_name in AVAILABLE_LISTENERS:
random_name_app, listener = create_listener(listener_name, output)
**{listener_name: create_listener(listener_name, output) random_name_app.listener(listener_name)(listener)
for listener_name in AVAILABLE_LISTENERS}) start_stop_app(random_name_app)
for listener_name in AVAILABLE_LISTENERS: for listener_name in AVAILABLE_LISTENERS:
assert random_name_app.name + listener_name == output.pop() assert random_name_app.name + listener_name == output.pop()

View File

@ -27,10 +27,11 @@ def test_register_system_signals():
async def hello_route(request): async def hello_route(request):
return HTTPResponse() return HTTPResponse()
app.run(HOST, PORT, app.listener('after_server_start')(stop)
before_start=set_loop, app.listener('before_server_start')(set_loop)
after_start=stop, app.listener('after_server_stop')(after)
after_stop=after)
app.run(HOST, PORT)
assert calledq.get() == True assert calledq.get() == True
@ -42,9 +43,9 @@ def test_dont_register_system_signals():
async def hello_route(request): async def hello_route(request):
return HTTPResponse() return HTTPResponse()
app.run(HOST, PORT, app.listener('after_server_start')(stop)
before_start=set_loop, app.listener('before_server_start')(set_loop)
after_start=stop, app.listener('after_server_stop')(after)
after_stop=after,
register_sys_signals=False) app.run(HOST, PORT, register_sys_signals=False)
assert calledq.get() == False assert calledq.get() == False