Fix examples to work as expected (#2305)

* Fix examples to work as expected

* Clean up examples

* Update worker test

* Merge in from main and cleanup example
This commit is contained in:
Adam Hopkins 2021-11-23 23:00:25 +02:00 committed by GitHub
parent 2c03eee329
commit 55c36e0240
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 267 additions and 174 deletions

View File

@ -4,12 +4,14 @@ import asyncio
from sanic import Sanic
app = Sanic()
app = Sanic(__name__)
async def notify_server_started_after_five_seconds():
await asyncio.sleep(5)
print('Server successfully started!')
print("Server successfully started!")
app.add_task(notify_server_started_after_five_seconds())

View File

@ -1,30 +1,29 @@
from sanic import Sanic
from sanic.response import text
from random import randint
app = Sanic()
from sanic import Sanic
from sanic.response import text
@app.middleware('request')
app = Sanic(__name__)
@app.middleware("request")
def append_request(request):
# Add new key with random value
request['num'] = randint(0, 100)
request.ctx.num = randint(0, 100)
@app.get('/pop')
@app.get("/pop")
def pop_handler(request):
# Pop key from request object
num = request.pop('num')
return text(num)
return text(request.ctx.num)
@app.get('/key_exist')
@app.get("/key_exist")
def key_exist_handler(request):
# Check the key is exist or not
if 'num' in request:
return text('num exist in request')
if hasattr(request.ctx, "num"):
return text("num exist in request")
return text('num does not exist in reqeust')
return text("num does not exist in reqeust")
app.run(host="0.0.0.0", port=8000, debug=True)

View File

@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
from sanic import Sanic
from functools import wraps
from sanic import Sanic
from sanic.response import json
app = Sanic()
app = Sanic(__name__)
def check_request_for_authorization_status(request):
@ -27,14 +29,16 @@ def authorized(f):
return response
else:
# the user is not authorized.
return json({'status': 'not_authorized'}, 403)
return json({"status": "not_authorized"}, 403)
return decorated_function
@app.route("/")
@authorized
async def test(request):
return json({'status': 'authorized'})
return json({"status": "authorized"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)

View File

@ -1,43 +1,53 @@
from sanic import Sanic, Blueprint
from sanic import Blueprint, Sanic
from sanic.response import text
'''
"""
Demonstrates that blueprint request middleware are executed in the order they
are added. And blueprint response middleware are executed in _reverse_ order.
On a valid request, it should print "1 2 3 6 5 4" to terminal
'''
"""
app = Sanic(__name__)
bp = Blueprint("bp_" + __name__)
@bp.middleware('request')
@bp.on_request
def request_middleware_1(request):
print('1')
print("1")
@bp.middleware('request')
@bp.on_request
def request_middleware_2(request):
print('2')
print("2")
@bp.middleware('request')
@bp.on_request
def request_middleware_3(request):
print('3')
print("3")
@bp.middleware('response')
@bp.on_response
def resp_middleware_4(request, response):
print('4')
print("4")
@bp.middleware('response')
@bp.on_response
def resp_middleware_5(request, response):
print('5')
print("5")
@bp.middleware('response')
@bp.on_response
def resp_middleware_6(request, response):
print('6')
print("6")
@bp.route('/')
@bp.route("/")
def pop_handler(request):
return text('hello world')
return text("hello world")
app.blueprint(bp, url_prefix='/bp')
app.blueprint(bp, url_prefix="/bp")
app.run(host="0.0.0.0", port=8000, debug=True, auto_reload=False)

View File

@ -1,6 +1,7 @@
from sanic import Blueprint, Sanic
from sanic.response import file, json
app = Sanic(__name__)
blueprint = Blueprint("name", url_prefix="/my_blueprint")
blueprint2 = Blueprint("name2", url_prefix="/my_blueprint2")

View File

@ -2,17 +2,20 @@ from asyncio import sleep
from sanic import Sanic, response
app = Sanic(__name__, strict_slashes=True)
@app.get("/")
async def handler(request):
return response.redirect("/sleep/3")
@app.get("/sleep/<t:number>")
async def handler2(request, t=0.3):
await sleep(t)
return response.text(f"Slept {t:.1f} seconds.\n")
if __name__ == '__main__':
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)

View File

@ -7,8 +7,10 @@ and pass in an instance of it when we create our Sanic instance. Inside this
class' default handler, we can do anything including sending exceptions to
an external service.
"""
from sanic.handlers import ErrorHandler
from sanic.exceptions import SanicException
from sanic.handlers import ErrorHandler
"""
Imports and code relevant for our CustomHandler class
(Ordinarily this would be in a separate file)
@ -16,7 +18,6 @@ Imports and code relevant for our CustomHandler class
class CustomHandler(ErrorHandler):
def default(self, request, exception):
# Here, we have access to the exception object
# and can do anything with it (log, send to external service, etc)
@ -38,17 +39,17 @@ server's error_handler to an instance of our CustomHandler
from sanic import Sanic
app = Sanic(__name__)
handler = CustomHandler()
app.error_handler = handler
app = Sanic(__name__, error_handler=handler)
@app.route("/")
async def test(request):
# Here, something occurs which causes an unexpected exception
# This exception will flow to our custom handler.
raise SanicException('You Broke It!')
raise SanicException("You Broke It!")
if __name__ == '__main__':
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, debug=True)

View File

@ -1,4 +1,6 @@
from sanic import Sanic, response, text
from sanic.handlers import ErrorHandler
from sanic.server.async_server import AsyncioServer
HTTP_PORT = 9999
@ -32,20 +34,40 @@ def proxy(request, path):
return response.redirect(url)
@https.listener("main_process_start")
@https.main_process_start
async def start(app, _):
global http
app.http_server = await http.create_server(
http_server = await http.create_server(
port=HTTP_PORT, return_asyncio_server=True
)
app.http_server.after_start()
app.add_task(runner(http, http_server))
app.ctx.http_server = http_server
app.ctx.http = http
@https.listener("main_process_stop")
@https.main_process_stop
async def stop(app, _):
app.http_server.before_stop()
await app.http_server.close()
app.http_server.after_stop()
await app.ctx.http_server.before_stop()
await app.ctx.http_server.close()
for connection in app.ctx.http_server.connections:
connection.close_if_idle()
await app.ctx.http_server.after_stop()
app.ctx.http = False
async def runner(app: Sanic, app_server: AsyncioServer):
app.is_running = True
try:
app.signalize()
app.finalize()
ErrorHandler.finalize(app.error_handler)
app_server.init = True
await app_server.before_start()
await app_server.after_start()
await app_server.serve_forever()
finally:
app.is_running = False
app.is_stopping = True
https.run(port=HTTPS_PORT, debug=True)

View File

@ -1,26 +1,30 @@
import asyncio
import httpx
from sanic import Sanic
from sanic.response import json
import asyncio
import aiohttp
app = Sanic(__name__)
sem = None
@app.listener('before_server_start')
def init(sanic, loop):
@app.before_server_start
def init(sanic, _):
global sem
concurrency_per_worker = 4
sem = asyncio.Semaphore(concurrency_per_worker, loop=loop)
sem = asyncio.Semaphore(concurrency_per_worker)
async def bounded_fetch(session, url):
"""
Use session object to perform 'get' request on url
"""
async with sem, session.get(url) as response:
return await response.json()
async with sem:
response = await session.get(url)
return response.json()
@app.route("/")
@ -28,9 +32,9 @@ async def test(request):
"""
Download and serve example JSON
"""
url = "https://api.github.com/repos/channelcat/sanic"
url = "https://api.github.com/repos/sanic-org/sanic"
async with aiohttp.ClientSession() as session:
async with httpx.AsyncClient() as session:
response = await bounded_fetch(session, url)
return json(response)

View File

@ -1,6 +1,6 @@
import logging
import aiotask_context as context
from contextvars import ContextVar
from sanic import Sanic, response
@ -11,8 +11,8 @@ log = logging.getLogger(__name__)
class RequestIdFilter(logging.Filter):
def filter(self, record):
try:
record.request_id = context.get("X-Request-ID")
except ValueError:
record.request_id = app.ctx.request_id.get(None) or "n/a"
except AttributeError:
record.request_id = "n/a"
return True
@ -49,8 +49,7 @@ app = Sanic(__name__, log_config=LOG_SETTINGS)
@app.on_request
async def set_request_id(request):
request_id = request.id
context.set("X-Request-ID", request_id)
request.app.ctx.request_id.set(request.id)
log.info(f"Setting {request.id=}")
@ -61,14 +60,14 @@ async def set_request_header(request, response):
@app.route("/")
async def test(request):
log.debug("X-Request-ID: %s", context.get("X-Request-ID"))
log.debug("X-Request-ID: %s", request.id)
log.info("Hello from test!")
return response.json({"test": True})
@app.before_server_start
def setup(app, loop):
loop.set_task_factory(context.task_factory)
app.ctx.request_id = ContextVar("request_id")
if __name__ == "__main__":

View File

@ -1,5 +1,6 @@
import logging
import socket
from os import getenv
from platform import node
from uuid import getnode as get_mac
@ -7,10 +8,11 @@ from uuid import getnode as get_mac
from logdna import LogDNAHandler
from sanic import Sanic
from sanic.response import json
from sanic.request import Request
from sanic.response import json
log = logging.getLogger('logdna')
log = logging.getLogger("logdna")
log.setLevel(logging.INFO)
@ -30,10 +32,12 @@ logdna_options = {
"index_meta": True,
"hostname": node(),
"ip": get_my_ip_address(),
"mac": get_mac_address()
"mac": get_mac_address(),
}
logdna_handler = LogDNAHandler(getenv("LOGDNA_API_KEY"), options=logdna_options)
logdna_handler = LogDNAHandler(
getenv("LOGDNA_API_KEY"), options=logdna_options
)
logdna = logging.getLogger(__name__)
logdna.setLevel(logging.INFO)
@ -49,13 +53,8 @@ def log_request(request: Request):
@app.route("/")
def default(request):
return json({
"response": "I was here"
})
return json({"response": "I was here"})
if __name__ == "__main__":
app.run(
host="0.0.0.0",
port=getenv("PORT", 8080)
)
app.run(host="0.0.0.0", port=getenv("PORT", 8080))

View File

@ -59,31 +59,31 @@ async def handler_stream(request):
return response.stream(body)
@app.listener("before_server_start")
@app.before_server_start
async def listener_before_server_start(*args, **kwargs):
print("before_server_start")
@app.listener("after_server_start")
@app.after_server_start
async def listener_after_server_start(*args, **kwargs):
print("after_server_start")
@app.listener("before_server_stop")
@app.before_server_stop
async def listener_before_server_stop(*args, **kwargs):
print("before_server_stop")
@app.listener("after_server_stop")
@app.after_server_stop
async def listener_after_server_stop(*args, **kwargs):
print("after_server_stop")
@app.middleware("request")
@app.on_request
async def print_on_request(request):
print("print_on_request")
@app.middleware("response")
@app.on_response
async def print_on_response(request, response):
print("print_on_response")

View File

@ -1,9 +1,12 @@
from sanic import Sanic
from sanic import response
from signal import signal, SIGINT
import asyncio
from signal import SIGINT, signal
import uvloop
from sanic import Sanic, response
app = Sanic(__name__)
@ -11,12 +14,18 @@ app = Sanic(__name__)
async def test(request):
return response.json({"answer": "42"})
asyncio.set_event_loop(uvloop.new_event_loop())
server = app.create_server(host="0.0.0.0", port=8000, return_asyncio_server=True)
server = app.create_server(
host="0.0.0.0", port=8000, return_asyncio_server=True
)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(server)
server = loop.run_until_complete(task)
loop.run_until_complete(server.startup())
signal(SIGINT, lambda s, f: loop.stop())
try:
loop.run_forever()
except:
finally:
loop.stop()

View File

@ -11,9 +11,24 @@ from sanic.server import AsyncioServer
app = Sanic(__name__)
@app.listener("after_server_start")
async def after_start_test(app, loop):
print("Async Server Started!")
@app.before_server_start
async def before_server_start(app, loop):
print("Async Server starting")
@app.after_server_start
async def after_server_start(app, loop):
print("Async Server started")
@app.before_server_stop
async def before_server_stop(app, loop):
print("Async Server stopping")
@app.after_server_stop
async def after_server_stop(app, loop):
print("Async Server stopped")
@app.route("/")
@ -28,20 +43,20 @@ serv_coro = app.create_server(
loop = asyncio.get_event_loop()
serv_task = asyncio.ensure_future(serv_coro, loop=loop)
signal(SIGINT, lambda s, f: loop.stop())
server: AsyncioServer = loop.run_until_complete(serv_task) # type: ignore
server.startup()
server: AsyncioServer = loop.run_until_complete(serv_task)
loop.run_until_complete(server.startup())
# When using app.run(), this actually triggers before the serv_coro.
# But, in this example, we are using the convenience method, even if it is
# out of order.
server.before_start()
server.after_start()
loop.run_until_complete(server.before_start())
loop.run_until_complete(server.after_start())
try:
loop.run_forever()
except KeyboardInterrupt:
loop.stop()
finally:
server.before_stop()
loop.run_until_complete(server.before_stop())
# Wait for server to close
close_task = server.close()
@ -50,4 +65,4 @@ finally:
# Complete all tasks on the loop
for connection in server.connections:
connection.close_if_idle()
server.after_stop()
loop.run_until_complete(server.after_stop())

View File

@ -1,42 +1,41 @@
from sanic import Sanic
from sanic.views import HTTPMethodView
from sanic.response import text
from sanic.views import HTTPMethodView
app = Sanic('some_name')
app = Sanic("some_name")
class SimpleView(HTTPMethodView):
def get(self, request):
return text('I am get method')
return text("I am get method")
def post(self, request):
return text('I am post method')
return text("I am post method")
def put(self, request):
return text('I am put method')
return text("I am put method")
def patch(self, request):
return text('I am patch method')
return text("I am patch method")
def delete(self, request):
return text('I am delete method')
return text("I am delete method")
class SimpleAsyncView(HTTPMethodView):
async def get(self, request):
return text('I am async get method')
return text("I am async get method")
async def post(self, request):
return text('I am async post method')
return text("I am async post method")
async def put(self, request):
return text('I am async put method')
return text("I am async put method")
app.add_route(SimpleView.as_view(), '/')
app.add_route(SimpleAsyncView.as_view(), '/async')
app.add_route(SimpleView.as_view(), "/")
app.add_route(SimpleAsyncView.as_view(), "/async")
if __name__ == '__main__':
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, debug=True)

View File

@ -1,9 +1,9 @@
import os
from sanic import Sanic
from sanic.log import logger as log
from sanic import response
from sanic import Sanic, response
from sanic.exceptions import ServerError
from sanic.log import logger as log
app = Sanic(__name__)
@ -13,7 +13,7 @@ async def test_async(request):
return response.json({"test": True})
@app.route("/sync", methods=['GET', 'POST'])
@app.route("/sync", methods=["GET", "POST"])
def test_sync(request):
return response.json({"test": True})
@ -31,6 +31,7 @@ def exception(request):
@app.route("/await")
async def test_await(request):
import asyncio
await asyncio.sleep(5)
return response.text("I'm feeling sleepy")
@ -42,8 +43,10 @@ async def test_file(request):
@app.route("/file_stream")
async def test_file_stream(request):
return await response.file_stream(os.path.abspath("setup.py"),
chunk_size=1024)
return await response.file_stream(
os.path.abspath("setup.py"), chunk_size=1024
)
# ----------------------------------------------- #
# Exceptions
@ -52,14 +55,17 @@ async def test_file_stream(request):
@app.exception(ServerError)
async def test(request, exception):
return response.json({"exception": "{}".format(exception), "status": exception.status_code},
status=exception.status_code)
return response.json(
{"exception": str(exception), "status": exception.status_code},
status=exception.status_code,
)
# ----------------------------------------------- #
# Read from request
# ----------------------------------------------- #
@app.route("/json")
def post_json(request):
return response.json({"received": True, "message": request.json})
@ -67,38 +73,51 @@ def post_json(request):
@app.route("/form")
def post_form_json(request):
return response.json({"received": True, "form_data": request.form, "test": request.form.get('test')})
return response.json(
{
"received": True,
"form_data": request.form,
"test": request.form.get("test"),
}
)
@app.route("/query_string")
def query_string(request):
return response.json({"parsed": True, "args": request.args, "url": request.url,
"query_string": request.query_string})
return response.json(
{
"parsed": True,
"args": request.args,
"url": request.url,
"query_string": request.query_string,
}
)
# ----------------------------------------------- #
# Run Server
# ----------------------------------------------- #
@app.listener('before_server_start')
@app.before_server_start
def before_start(app, loop):
log.info("SERVER STARTING")
@app.listener('after_server_start')
@app.after_server_start
def after_start(app, loop):
log.info("OH OH OH OH OHHHHHHHH")
@app.listener('before_server_stop')
@app.before_server_stop
def before_stop(app, loop):
log.info("SERVER STOPPING")
@app.listener('after_server_stop')
@app.after_server_stop
def after_stop(app, loop):
log.info("TRIED EVERYTHING")
if __name__ == '__main__':
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, debug=True)

View File

@ -1,7 +1,8 @@
from sanic import Sanic
from sanic import response
import socket
import os
import socket
from sanic import Sanic, response
app = Sanic(__name__)
@ -10,8 +11,9 @@ app = Sanic(__name__)
async def test(request):
return response.text("OK")
if __name__ == '__main__':
server_address = './uds_socket'
if __name__ == "__main__":
server_address = "./uds_socket"
# Make sure the socket does not already exist
try:
os.unlink(server_address)

View File

@ -1,20 +1,21 @@
from sanic import Sanic
from sanic import response
from sanic import Sanic, response
app = Sanic(__name__)
@app.route('/')
@app.route("/")
async def index(request):
# generate a URL for the endpoint `post_handler`
url = app.url_for('post_handler', post_id=5)
url = app.url_for("post_handler", post_id=5)
# the URL is `/posts/5`, redirect to it
return response.redirect(url)
@app.route('/posts/<post_id>')
@app.route("/posts/<post_id>")
async def post_handler(request, post_id):
return response.text('Post - {}'.format(post_id))
return response.text("Post - {}".format(post_id))
if __name__ == '__main__':
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, debug=True)

View File

@ -8,7 +8,9 @@ app = Sanic(name="blue-print-group-version-example")
bp1 = Blueprint(name="ultron", url_prefix="/ultron")
bp2 = Blueprint(name="vision", url_prefix="/vision", strict_slashes=None)
bpg = Blueprint.group([bp1, bp2], url_prefix="/sentient/robot", version=1, strict_slashes=True)
bpg = Blueprint.group(
bp1, bp2, url_prefix="/sentient/robot", version=1, strict_slashes=True
)
@bp1.get("/name")
@ -31,5 +33,5 @@ async def bp2_revised_name(request):
app.blueprint(bpg)
if __name__ == '__main__':
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)

View File

@ -1,25 +1,27 @@
from sanic import Sanic
from sanic.response import redirect
app = Sanic(__name__)
app.static('index.html', "websocket.html")
app.static("index.html", "websocket.html")
@app.route('/')
@app.route("/")
def index(request):
return redirect("index.html")
@app.websocket('/feed')
@app.websocket("/feed")
async def feed(request, ws):
while True:
data = 'hello!'
print('Sending: ' + data)
data = "hello!"
print("Sending: " + data)
await ws.send(data)
data = await ws.recv()
print('Received: ' + data)
print("Received: " + data)
if __name__ == '__main__':
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, debug=True)

View File

@ -52,7 +52,7 @@ class RouteMixin:
self,
uri: str,
methods: Optional[Iterable[str]] = None,
host: Optional[str] = None,
host: Optional[Union[str, List[str]]] = None,
strict_slashes: Optional[bool] = None,
stream: bool = False,
version: Optional[Union[int, str, float]] = None,
@ -189,7 +189,7 @@ class RouteMixin:
handler: RouteHandler,
uri: str,
methods: Iterable[str] = frozenset({"GET"}),
host: Optional[str] = None,
host: Optional[Union[str, List[str]]] = None,
strict_slashes: Optional[bool] = None,
version: Optional[Union[int, str, float]] = None,
name: Optional[str] = None,
@ -254,7 +254,7 @@ class RouteMixin:
def get(
self,
uri: str,
host: Optional[str] = None,
host: Optional[Union[str, List[str]]] = None,
strict_slashes: Optional[bool] = None,
version: Optional[Union[int, str, float]] = None,
name: Optional[str] = None,
@ -290,7 +290,7 @@ class RouteMixin:
def post(
self,
uri: str,
host: Optional[str] = None,
host: Optional[Union[str, List[str]]] = None,
strict_slashes: Optional[bool] = None,
stream: bool = False,
version: Optional[Union[int, str, float]] = None,
@ -326,7 +326,7 @@ class RouteMixin:
def put(
self,
uri: str,
host: Optional[str] = None,
host: Optional[Union[str, List[str]]] = None,
strict_slashes: Optional[bool] = None,
stream: bool = False,
version: Optional[Union[int, str, float]] = None,
@ -362,7 +362,7 @@ class RouteMixin:
def head(
self,
uri: str,
host: Optional[str] = None,
host: Optional[Union[str, List[str]]] = None,
strict_slashes: Optional[bool] = None,
version: Optional[Union[int, str, float]] = None,
name: Optional[str] = None,
@ -406,7 +406,7 @@ class RouteMixin:
def options(
self,
uri: str,
host: Optional[str] = None,
host: Optional[Union[str, List[str]]] = None,
strict_slashes: Optional[bool] = None,
version: Optional[Union[int, str, float]] = None,
name: Optional[str] = None,
@ -450,7 +450,7 @@ class RouteMixin:
def patch(
self,
uri: str,
host: Optional[str] = None,
host: Optional[Union[str, List[str]]] = None,
strict_slashes: Optional[bool] = None,
stream=False,
version: Optional[Union[int, str, float]] = None,
@ -496,7 +496,7 @@ class RouteMixin:
def delete(
self,
uri: str,
host: Optional[str] = None,
host: Optional[Union[str, List[str]]] = None,
strict_slashes: Optional[bool] = None,
version: Optional[Union[int, str, float]] = None,
name: Optional[str] = None,
@ -532,7 +532,7 @@ class RouteMixin:
def websocket(
self,
uri: str,
host: Optional[str] = None,
host: Optional[Union[str, List[str]]] = None,
strict_slashes: Optional[bool] = None,
subprotocols: Optional[List[str]] = None,
version: Optional[Union[int, str, float]] = None,
@ -573,7 +573,7 @@ class RouteMixin:
self,
handler,
uri: str,
host: Optional[str] = None,
host: Optional[Union[str, List[str]]] = None,
strict_slashes: Optional[bool] = None,
subprotocols=None,
version: Optional[Union[int, str, float]] = None,

View File

@ -13,7 +13,7 @@ class FutureRoute(NamedTuple):
handler: str
uri: str
methods: Optional[Iterable[str]]
host: str
host: Union[str, List[str]]
strict_slashes: bool
stream: bool
version: Optional[int]

View File

@ -21,7 +21,7 @@ def gunicorn_worker():
"gunicorn "
f"--bind 127.0.0.1:{PORT} "
"--worker-class sanic.worker.GunicornWorker "
"examples.simple_server:app"
"examples.hello_world:app"
)
worker = subprocess.Popen(shlex.split(command))
time.sleep(2)
@ -35,7 +35,7 @@ def gunicorn_worker_with_access_logs():
"gunicorn "
f"--bind 127.0.0.1:{PORT + 1} "
"--worker-class sanic.worker.GunicornWorker "
"examples.simple_server:app"
"examples.hello_world:app"
)
worker = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
time.sleep(2)
@ -50,7 +50,7 @@ def gunicorn_worker_with_env_var():
f"--bind 127.0.0.1:{PORT + 2} "
"--worker-class sanic.worker.GunicornWorker "
"--log-level info "
"examples.simple_server:app"
"examples.hello_world:app"
)
worker = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
time.sleep(2)