Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何从因超时而取消的python asyncio协同路由返回值_Python_Python 3.x_Exception_Python Asyncio_Cancellation - Fatal编程技术网

如何从因超时而取消的python asyncio协同路由返回值

如何从因超时而取消的python asyncio协同路由返回值,python,python-3.x,exception,python-asyncio,cancellation,Python,Python 3.x,Exception,Python Asyncio,Cancellation,在python>3.5中,由于TimeoutError而取消后,如何让协同程序返回最终值 我有一个小型python项目,它使用多个协程来传输数据,并报告传输的数据量。它需要一个超时参数;如果脚本在完成转移之前超时,它会报告在取消之前转移的金额 它在python3.5中运行良好,但最近我尝试更新到3.8,但遇到了麻烦 下面是示例代码,其行为明显不同于3.5、3.6、3.7和3.8: 导入异步IO 导入系统 异步def foo(): 尝试: 等待异步睡眠(10) 除asyncio.Cancelled

在python>3.5中,由于TimeoutError而取消后,如何让协同程序返回最终值

我有一个小型python项目,它使用多个协程来传输数据,并报告传输的数据量。它需要一个超时参数;如果脚本在完成转移之前超时,它会报告在取消之前转移的金额

它在python3.5中运行良好,但最近我尝试更新到3.8,但遇到了麻烦

下面是示例代码,其行为明显不同于3.5、3.6、3.7和3.8:

导入异步IO
导入系统
异步def foo():
尝试:
等待异步睡眠(10)
除asyncio.Cancelled错误外:
打印(“foo被取消”)
返回1
异步def main():
coros=asyncio.gather(*(foo()表示范围(3)中的uu)))
尝试:
等待异步IO。等待(coros,超时=0.1)
除asyncio.TimeoutError外:
打印(“主协同程序超时”)
等待科罗斯
返回coros.result()
如果名称=“\uuuuu main\uuuuuuuu”:
打印(系统版本)
loop=asyncio.new\u event\u loop()
尝试:
结果=循环。运行\u直到完成(main())
打印(“结果:{}”。格式(结果))
例外情况除外,如e:
打印(“主目录中的异常”)
打印(e)
最后:
loop.close()
$3.5 3.6 3.7 3.8版本;做回音;python${ver}example.py;完成
3.5.7(默认,2019年9月6日,07:49:56)
[GCC 4.2.1兼容苹果LLVM 10.0.1(clang-1001.0.46.4)]
主协同程序超时
福被取消了
福被取消了
福被取消了
结果:[1,1,1]
3.6.9(默认,2019年9月6日,07:45:14)
[GCC 4.2.1兼容苹果LLVM 10.0.1(clang-1001.0.46.4)]
主协同程序超时
福被取消了
福被取消了
福被取消了
__umain__;中的异常:
3.7.4(默认,2019年9月17日13:46:30)
[Clang 10.0.1(Clang-1001.0.46.4)]
福被取消了
福被取消了
福被取消了
主协同程序超时
__umain__;中的异常:
3.8.0(默认,2019年10月16日21:30:17)
[铿锵11.0.0(铿锵-1100.0.33.8)]
福被取消了
福被取消了
福被取消了
主协同程序超时
回溯(最近一次呼叫最后一次):
文件“example.py”,第28行,在
结果=循环。运行\u直到完成(main())
文件“/usr/local/var/pyenv/versions/3.8.0/lib/python3.8/asyncio/base_events.py”,第608行,运行_直到完成
返回future.result()
asyncio.exceptions.CancelleError
由于
取消错误
现在是
BaseException
而不是
exception
(编辑:这可能是回溯在此处打印,而不是在其他地方打印的原因),因此
main中的
异常不适用于3.8

我已经尝试了在
asyncio.gather
中使用
return\u exceptions=True
的许多配置,或者在
中捕获
cancelederror
,除了asyncio.TimeoutError:
块,但我似乎无法正确地获取它

我需要将
main
保留为一个异步函数,因为在我的实际代码中,它正在创建一个aiohttp会话供其他协同程序共享,而现代的aiohttp要求在异步上下文管理器(而不是常规的同步上下文管理器)中完成

我希望代码能够在3.5-3.8上运行,所以我没有使用
asyncio.run

我试过使用
.cancel()
contextlib.suppress(asyncio.cancelederror)或不带
contextlib.suppress(asyncio.cancelederror)
的许多其他问题的代码,但还是没有成功。我还尝试返回一个等待的值(例如
result=await coros;return result
而不是
return coros.result()
),也没有骰子

在python>3.5中,有没有一个好方法可以让我获得python 3.5的行为,在超时时我可以有一个corroutine catch
cancelederror
,并在下次等待时返回一个值


提前感谢。

我做了一些调试,结果似乎在asyncio.gather取消的情况下从未设置过,因此无法从python 3.8中的
\u GatheringFuture
对象中检索它

asyncio/tasks.py:792

            if outer._cancel_requested:
                # If gather is being cancelled we must propagate the
                # cancellation regardless of *return_exceptions* argument.
                # See issue 32684.
                outer.set_exception(exceptions.CancelledError())
            else:
                outer.set_result(results)
在阅读文档时,我发现了有关
asyncio.CancelledError

在几乎所有情况下,都必须重新引发异常

在我看来,Python3.5的行为是相当无意的。我不会相信的

虽然可以通过不使用
asyncio.gather
来解决这个问题,但这并不值得。如果您确实需要从取消的协程中获得部分结果,则只需将其添加到某个全局列表中:

    except asyncio.CancelledError:
        print("foo got cancelled")
        global_results.append(1)
        raise

感谢@RafalS和他们建议停止使用
asyncio.gather

与使用
gather
wait\u for
不同,似乎直接使用
.wait
中的超时与协同程序可能是最好的选择,可以在3.5到3.8之间工作

请注意,下面的bash命令稍作修改,以显示任务正在同时运行,并且在不等待
foo
完成的情况下被取消

导入异步IO
导入系统
异步def foo():
尝试:
等待异步睡眠(10)
除asyncio.Cancelled错误外:
通过
最后:
返回1
异步def main():
coros=[foo()表示范围(3)内的uu]
完成,挂起=等待异步IO.wait(coros,超时=1.0)
对于挂起中的任务:
task.cancel()
等待任务
返回[task.result(),用于已完成的任务|挂起]
如果名称=“\uuuuu main\uuuuuuuu”:
打印(系统版本)
loop=asyncio.new\u event\u loop()
尝试:
结果=循环。运行\u直到完成(main())
打印(“结果:{}”。格式(结果))
最后:
loop.close()
$3.5 3.6 3.7 3.8版本;做回音;时间python${ver}example.py;完成
3.5.7(默认,2019年9月6日,07:49:56)
[GCC 4.2.1兼容苹果LLVM 10.0.1(clang-1001.0.46.4)]
结果:[1,1,1]
真正的0m1.634s
用户0