好吧,这很奇怪,但这是-
import asyncio
from starlette.applications import Starlette
class MyTasks:
def __init__(self):
self.task = None
async def main(self):
self.task = asyncio.create_task(self.hello())
async def hello(self):
raise ValueError
async def main():
await MyTasks().main()
app = Starlette(on_startup=[main])
$ uvicorn test:app
INFO: Started server process [26622]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
嗯,不ValueError
这里
现在,删除MyTasks.main()
中对self.task
的赋值
async def main(self):
asyncio.create_task(self.hello())
...
$ uvicorn test:app
INFO: Started server process [29083]
INFO: Waiting for application startup.
ERROR: Task exception was never retrieved
future: <Task finished name='Task-3' coro=<MyTasks.hello() done, defined at ./test.py:13> exception=ValueError()>
Traceback (most recent call last):
File "./test.py", line 14, in hello
raise ValueError
ValueError
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
。。。瞧
这是怎么回事?这个分配是如何实现或破坏异常日志记录的
任务是future的子类,这意味着它具有结果的概念。对于任务,结果是它驱动的协同程序返回的值。如果协同程序引发异常,则异常将封装在任务对象中。正确编写的代码最终会等待任务或访问其结果,以便异常不会以静默方式传递
为了帮助调试忘记访问任务的代码,如果任务引发异常但从未等待,则任务的析构函数将记录错误。在运行析构函数之前,不能记录此错误,因为只要代码保持任务对象不变,它可能会在任何时候等待它。析构函数的运行点是Python能够可靠地“证明”任务未等待的第一个实例
但这种最后的日志记录不是您应该依赖的,它是在尽力而为的基础上提供的。例如,由于GC的原因,运行析构函数可能会被推迟。我希望将任务分配给其绑定方法由任务驱动的实例会使任务成为引用周期的一部分。这会将析构函数的运行推迟到完全GC,并且您不会看到日志
要解决这个问题,您应该在协同程序中捕获异常并自己记录它们,而不是允许它们传播,或者在代码中的某个点实际等待任务
对不起,这是个骗局
替换为
asyncio.create_task()
has been posted there的人数减少这是使用替换后的输出
create_task()
案例1:与
await self.task
案例2:没有
await self.task
相关问题 更多 >
编程相关推荐