This seems to be possible because in app.Sanic.handle_request()
there's this snippet:
if isawaitable(response):
response = await response
And this is how awaitable
is checked by Python:
def isawaitable(object):
"""Return true if object can be passed to an ``await`` expression."""
return (isinstance(object, types.CoroutineType) or
isinstance(object, types.GeneratorType) and
bool(object.gi_code.co_flags & CO_ITERABLE_COROUTINE) or
isinstance(object, collections.abc.Awaitable))
I know to use async def
to create an awaitable function, but I don't know how to create an awaitable HTTPResponse
instance. It would really help to see an example of an awaitable response with a simple await asyncio.sleep(5)
if possible.
Tried the solution by Mikhail, here's what I observed:
raise500
enters theasyncio.sleep()
ret500
does not enter theasyncio.sleep()
(bug)raise500
blocks otherraise500
(bug)raise500
does not blockret500
- Cannot tell if
ret500
would block otherret500
because it's too fast (not sleeping)
Full code (run by saving as test.py
, then in shell python test.py
and going to http://127.0.0.1:8000/api/test
):
import asyncio
from sanic import Sanic
from sanic.response import HTTPResponse
from sanic.handlers import ErrorHandler
class AsyncHTTPResponse(HTTPResponse): # make it awaitable
def __await__(self):
return self._coro().__await__() # see https://stackoverflow.com/a/33420721/1113207
async def _coro(self):
print('Sleeping')
await asyncio.sleep(5)
print('Slept 5 seconds')
return self
class CustomErrorHandler(ErrorHandler):
def response(self, request, exception):
return AsyncHTTPResponse(status=500)
app = Sanic(__name__, error_handler=CustomErrorHandler())
@app.get("/api/test")
async def test(request):
return HTTPResponse(status=204)
@app.get("/api/raise500")
async def raise500(request):
raise Exception
@app.get("/api/ret500")
async def ret500(request):
return AsyncHTTPResponse(status=500)
if __name__ == "__main__":
app.run()