这似乎是可能的,因为app.Sanic.handle_request()
有这个片段:
if isawaitable(response):
response = await response
这就是awaitable
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))
我知道用来async def
创建一个可等待的函数,但我不知道如何创建一个可等待的HTTPResponse
实例。如果可能的话,用一个简单的方法来查看一个可等待响应的示例真的很有帮助await asyncio.sleep(5)
。
尝试了 Mikhail 的解决方案,这是我观察到的:
raise500
进入asyncio.sleep()
ret500
不进asyncio.sleep()
(bug)raise500
阻止其他raise500
(错误)raise500
不阻塞ret500
- 无法判断是否
ret500
会阻止其他ret500
,因为它太快了(不睡觉)
完整代码(通过另存为 运行test.py
,然后在 shell 中python test.py
并转到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()