commit
ed5fe9ae9f
|
@ -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.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""
|
"""
|
||||||
Example of caching using aiocache package. To run it you will need a Redis
|
Example of caching using aiocache package. To run it you will need a Redis
|
||||||
instance running in localhost:6379.
|
instance running in localhost:6379. You can also try with SimpleMemoryCache.
|
||||||
|
|
||||||
Running this example you will see that the first call lasts 3 seconds and
|
Running this example you will see that the first call lasts 3 seconds and
|
||||||
the rest are instant because the value is retrieved from the Redis.
|
the rest are instant because the value is retrieved from the Redis.
|
||||||
|
@ -20,9 +20,14 @@ from aiocache.serializers import JsonSerializer
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic(__name__)
|
||||||
|
|
||||||
aiocache.settings.set_defaults(
|
|
||||||
class_="aiocache.RedisCache"
|
@app.listener('before_server_start')
|
||||||
)
|
def init_cache(sanic, loop):
|
||||||
|
aiocache.settings.set_defaults(
|
||||||
|
class_="aiocache.RedisCache",
|
||||||
|
# class_="aiocache.SimpleMemoryCache",
|
||||||
|
loop=loop
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@cached(key="my_custom_key", serializer=JsonSerializer())
|
@cached(key="my_custom_key", serializer=JsonSerializer())
|
||||||
|
@ -38,4 +43,4 @@ async def test(request):
|
||||||
return json(await expensive_call())
|
return json(await expensive_call())
|
||||||
|
|
||||||
|
|
||||||
app.run(host="0.0.0.0", port=8000, loop=asyncio.get_event_loop())
|
app.run(host="0.0.0.0", port=8000)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 there’s no need to connect and re-connect before executing async queries
|
# Also there’s no need to connect and re-connect before executing async queries
|
||||||
# with manager! It’s all automatic. But you can run Manager.connect() or
|
# with manager! It’s 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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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,16 +10,18 @@ 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
|
||||||
from .server import serve, serve_multiple, HttpProtocol
|
from .server import serve, serve_multiple, HttpProtocol
|
||||||
from .static import register as static_register
|
from .static import register as static_register
|
||||||
|
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
|
||||||
|
@ -44,7 +46,7 @@ class Sanic:
|
||||||
self._blueprint_order = []
|
self._blueprint_order = []
|
||||||
self.debug = None
|
self.debug = None
|
||||||
self.sock = None
|
self.sock = 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
|
||||||
|
@ -60,6 +62,34 @@ class Sanic:
|
||||||
# Registration
|
# Registration
|
||||||
# -------------------------------------------------------------------- #
|
# -------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
def add_task(self, task):
|
||||||
|
"""
|
||||||
|
Schedule a task to run later, after the loop has started.
|
||||||
|
Different from asyncio.ensure_future in that it does not
|
||||||
|
also return a future, and the actual ensure_future call
|
||||||
|
is delayed until before server start.
|
||||||
|
|
||||||
|
:param task: A future, couroutine or awaitable.
|
||||||
|
"""
|
||||||
|
@self.listener('before_server_start')
|
||||||
|
def run(app, loop):
|
||||||
|
if callable(task):
|
||||||
|
loop.create_task(task())
|
||||||
|
else:
|
||||||
|
loop.create_task(task)
|
||||||
|
|
||||||
|
# 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):
|
||||||
"""
|
"""
|
||||||
|
@ -120,7 +150,16 @@ class Sanic:
|
||||||
"""
|
"""
|
||||||
# Handle HTTPMethodView differently
|
# Handle HTTPMethodView differently
|
||||||
if hasattr(handler, 'view_class'):
|
if hasattr(handler, 'view_class'):
|
||||||
methods = frozenset(HTTP_METHODS)
|
methods = set()
|
||||||
|
|
||||||
|
for method in HTTP_METHODS:
|
||||||
|
if getattr(handler.view_class, method.lower(), None):
|
||||||
|
methods.add(method)
|
||||||
|
|
||||||
|
# handle composition view differently
|
||||||
|
if isinstance(handler, CompositionView):
|
||||||
|
methods = handler.handlers.keys()
|
||||||
|
|
||||||
self.route(uri=uri, methods=methods, host=host)(handler)
|
self.route(uri=uri, methods=methods, host=host)(handler)
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
|
@ -409,6 +448,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)
|
||||||
|
@ -463,6 +503,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
|
||||||
|
@ -487,19 +537,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
|
||||||
|
|
|
@ -346,7 +346,11 @@ def serve(host, port, request_handler, error_handler, before_start=None,
|
||||||
# Register signals for graceful termination
|
# Register signals for graceful termination
|
||||||
if register_sys_signals:
|
if register_sys_signals:
|
||||||
for _signal in (SIGINT, SIGTERM):
|
for _signal in (SIGINT, SIGTERM):
|
||||||
loop.add_signal_handler(_signal, loop.stop)
|
try:
|
||||||
|
loop.add_signal_handler(_signal, loop.stop)
|
||||||
|
except NotImplementedError:
|
||||||
|
log.warn(('Sanic tried to use loop.add_signal_handler')
|
||||||
|
('but it is not implemented on this platform.'))
|
||||||
|
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from .exceptions import InvalidUsage
|
from .exceptions import InvalidUsage
|
||||||
|
from .constants import HTTP_METHODS
|
||||||
|
|
||||||
|
|
||||||
class HTTPMethodView:
|
class HTTPMethodView:
|
||||||
|
@ -40,11 +41,7 @@ class HTTPMethodView:
|
||||||
|
|
||||||
def dispatch_request(self, request, *args, **kwargs):
|
def dispatch_request(self, request, *args, **kwargs):
|
||||||
handler = getattr(self, request.method.lower(), None)
|
handler = getattr(self, request.method.lower(), None)
|
||||||
if handler:
|
return handler(request, *args, **kwargs)
|
||||||
return handler(request, *args, **kwargs)
|
|
||||||
raise InvalidUsage(
|
|
||||||
'Method {} not allowed for URL {}'.format(
|
|
||||||
request.method, request.url), status_code=405)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def as_view(cls, *class_args, **class_kwargs):
|
def as_view(cls, *class_args, **class_kwargs):
|
||||||
|
@ -89,15 +86,15 @@ class CompositionView:
|
||||||
|
|
||||||
def add(self, methods, handler):
|
def add(self, methods, handler):
|
||||||
for method in methods:
|
for method in methods:
|
||||||
|
if method not in HTTP_METHODS:
|
||||||
|
raise InvalidUsage(
|
||||||
|
'{} is not a valid HTTP method.'.format(method))
|
||||||
|
|
||||||
if method in self.handlers:
|
if method in self.handlers:
|
||||||
raise KeyError(
|
raise InvalidUsage(
|
||||||
'Method {} already is registered.'.format(method))
|
'Method {} is already registered.'.format(method))
|
||||||
self.handlers[method] = handler
|
self.handlers[method] = handler
|
||||||
|
|
||||||
def __call__(self, request, *args, **kwargs):
|
def __call__(self, request, *args, **kwargs):
|
||||||
handler = self.handlers.get(request.method.upper(), None)
|
handler = self.handlers[request.method.upper()]
|
||||||
if handler is None:
|
|
||||||
raise InvalidUsage(
|
|
||||||
'Method {} not allowed for URL {}'.format(
|
|
||||||
request.method, request.url), status_code=405)
|
|
||||||
return handler(request, *args, **kwargs)
|
return handler(request, *args, **kwargs)
|
||||||
|
|
|
@ -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'
|
||||||
|
|
30
tests/test_create_task.py
Normal file
30
tests/test_create_task.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import sanic
|
||||||
|
from sanic.utils import sanic_endpoint_test
|
||||||
|
from sanic.response import text
|
||||||
|
from threading import Event
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
def test_create_task():
|
||||||
|
e = Event()
|
||||||
|
async def coro():
|
||||||
|
await asyncio.sleep(0.05)
|
||||||
|
e.set()
|
||||||
|
|
||||||
|
app = sanic.Sanic()
|
||||||
|
app.add_task(coro)
|
||||||
|
|
||||||
|
@app.route('/early')
|
||||||
|
def not_set(request):
|
||||||
|
return text(e.is_set())
|
||||||
|
|
||||||
|
@app.route('/late')
|
||||||
|
async def set(request):
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
return text(e.is_set())
|
||||||
|
|
||||||
|
|
||||||
|
request, response = sanic_endpoint_test(app, uri='/early')
|
||||||
|
assert response.body == b'False'
|
||||||
|
|
||||||
|
request, response = sanic_endpoint_test(app, uri='/late')
|
||||||
|
assert response.body == b'True'
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import pytest as pytest
|
import pytest as pytest
|
||||||
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
|
from sanic.exceptions import InvalidUsage
|
||||||
from sanic.response import text, HTTPResponse
|
from sanic.response import text, HTTPResponse
|
||||||
from sanic.views import HTTPMethodView
|
from sanic.views import HTTPMethodView, CompositionView
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.utils import sanic_endpoint_test
|
from sanic.utils import sanic_endpoint_test
|
||||||
|
@ -196,3 +197,65 @@ def test_with_decorator():
|
||||||
request, response = sanic_endpoint_test(app, method="get")
|
request, response = sanic_endpoint_test(app, method="get")
|
||||||
assert response.text == 'I am get method'
|
assert response.text == 'I am get method'
|
||||||
assert results[0] == 1
|
assert results[0] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_composition_view_rejects_incorrect_methods():
|
||||||
|
def foo(request):
|
||||||
|
return text('Foo')
|
||||||
|
|
||||||
|
view = CompositionView()
|
||||||
|
|
||||||
|
with pytest.raises(InvalidUsage) as e:
|
||||||
|
view.add(['GET', 'FOO'], foo)
|
||||||
|
|
||||||
|
assert str(e.value) == 'FOO is not a valid HTTP method.'
|
||||||
|
|
||||||
|
|
||||||
|
def test_composition_view_rejects_duplicate_methods():
|
||||||
|
def foo(request):
|
||||||
|
return text('Foo')
|
||||||
|
|
||||||
|
view = CompositionView()
|
||||||
|
|
||||||
|
with pytest.raises(InvalidUsage) as e:
|
||||||
|
view.add(['GET', 'POST', 'GET'], foo)
|
||||||
|
|
||||||
|
assert str(e.value) == 'Method GET is already registered.'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('method', HTTP_METHODS)
|
||||||
|
def test_composition_view_runs_methods_as_expected(method):
|
||||||
|
app = Sanic('test_composition_view')
|
||||||
|
|
||||||
|
view = CompositionView()
|
||||||
|
view.add(['GET', 'POST', 'PUT'], lambda x: text('first method'))
|
||||||
|
view.add(['DELETE', 'PATCH'], lambda x: text('second method'))
|
||||||
|
|
||||||
|
app.add_route(view, '/')
|
||||||
|
|
||||||
|
if method in ['GET', 'POST', 'PUT']:
|
||||||
|
request, response = sanic_endpoint_test(app, uri='/', method=method)
|
||||||
|
assert response.text == 'first method'
|
||||||
|
|
||||||
|
if method in ['DELETE', 'PATCH']:
|
||||||
|
request, response = sanic_endpoint_test(app, uri='/', method=method)
|
||||||
|
assert response.text == 'second method'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('method', HTTP_METHODS)
|
||||||
|
def test_composition_view_rejects_invalid_methods(method):
|
||||||
|
app = Sanic('test_composition_view')
|
||||||
|
|
||||||
|
view = CompositionView()
|
||||||
|
view.add(['GET', 'POST', 'PUT'], lambda x: text('first method'))
|
||||||
|
|
||||||
|
app.add_route(view, '/')
|
||||||
|
|
||||||
|
if method in ['GET', 'POST', 'PUT']:
|
||||||
|
request, response = sanic_endpoint_test(app, uri='/', method=method)
|
||||||
|
assert response.status == 200
|
||||||
|
assert response.text == 'first method'
|
||||||
|
|
||||||
|
if method in ['DELETE', 'PATCH']:
|
||||||
|
request, response = sanic_endpoint_test(app, uri='/', method=method)
|
||||||
|
assert response.status == 405
|
||||||
|
|
Loading…
Reference in New Issue
Block a user