Python 3.14 弃用了 asyncio policy:3.16 移除,应用代码改用 asyncio.run + loop_factory 就够
升到 3.14 之后第一次跑带 -W error 的测试,很容易在 set_event_loop_policy 这一行报错。这不是误报,是 policy 系统整套进入弃用期。结论先说:绝大多数应用代码根本不需要 policy,把入口换成 asyncio.run() 或 asyncio.Runner,需要特定 loop 实现时用 loop_factory 即可;只有自己管理 loop 生命周期的宿主(Jupyter 这类)才需要 new_event_loop/直接构造 loop。
升级后先看到的是这条警告
issue 讨论里 asvetlov 贴的 -We 输出原文:
File "/…/Lib/asyncio/events.py", line 785, in set_event_loop_policy
warnings._deprecated('asyncio.set_event_loop_policy', remove=(3,16))
File "/…/Lib/warnings.py", line 668, in _deprecated
warn(msg, DeprecationWarning, stacklevel=3)
DeprecationWarning: 'asyncio.set_event_loop_policy' is deprecated and slated for removal in Python 3.16
stacklevel=3 意味着警告指向调用方,也就是你的代码,而不是 asyncio 内部。
被弃用的 7 个名字,和两个行为变更
asyncio policy 文档 页首警告写明:policies 已弃用,将在 Python 3.16 移除,建议改用 asyncio.run() 或带 loop_factory 的 asyncio.Runner 来指定 loop 实现。
同样标为 Deprecated since 3.14、3.16 移除的还有:
asyncio.get_event_loop_policy()asyncio.set_event_loop_policy(policy)(传None恢复默认 policy)asyncio.AbstractEventLoopPolicyasyncio.DefaultEventLoopPolicy(Unix 用SelectorEventLoop,Windows 用ProactorEventLoop)asyncio.WindowsSelectorEventLoopPolicy(仅 Windows)asyncio.WindowsProactorEventLoopPolicy(仅 Windows)asyncio.set_event_loop
行为变更跟着一起来(3.16 或更晚生效):
asyncio.get_event_loop变成get_running_loop的别名;asyncio.new_event_loop忽略自定义 policy,变成asyncio.EventLoop的别名;asyncio.run和asyncio.Runner不再走 policy,只依赖loop_factory。
3.14 已经先动了一处:DefaultEventLoopPolicy 的 get_event_loop() 在没有已设置 loop 时改为抛 RuntimeError,此前是创建/返回一个 loop。
为什么弃用:policy 是全局的,loop 是每线程的
cpython issue #127949(2024-12-26 关闭)给的理由很直接:「policies no longer serve a real purpose. Loops are always per thread, there is no need to have a 'current loop' when no loop is currently running.」
policy 是个 per-context 的全局对象,语义上 GET/SET「当前 loop」,默认按线程,自定义 policy 还能改 context 的定义。这层间接在只有单个 running loop 的模型里没有存在价值,却长期是混乱来源。整套一起弃用也避免了半弃用状态——3.10.0–3.10.8 和 3.11.0 曾错误地弃用 get_event_loop,后来因为 set_event_loop_policy 当时没弃用而回滚。
迁移:policy 三步换成直接构造 loop
issue 里给出的旧写法:
import asyncio
asyncio.set_event_loop_policy(MyPolicy())
policy = asyncio.get_event_loop_policy()
loop = policy.new_event_loop()
loop.run_until_complete(task)
需要自己控制 loop 时,直接实例化:
loop = MyCustomEventLoop()
loop.run_until_complete(task)
应用入口则交给 asyncio.run,用 loop_factory 指定实现(Doc/deprecations/pending-removal-in-3.16.rst 里的示例):
import asyncio
async def main():
...
asyncio.run(main(), loop_factory=asyncio.SelectorEventLoop)
或者用 Runner 管理生命周期:
import asyncio
async def main():
...
with asyncio.Runner(loop_factory=asyncio.SelectorEventLoop) as runner:
runner.run(main())
coroutine 和 callback 内部一律用 get_running_loop();eventloop 文档 也是这么建议的:优先 get_running_loop(),更高层用 asyncio.run(),不要手工创建/关闭 loop。
子类化才告警
弃用的类是被子类化时才发警告,和 child watchers 的处理方式一致。也就是说,文档里那段旧示例:
class MyEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
def get_event_loop(self):
loop = super().get_event_loop()
return loop
asyncio.set_event_loop_policy(MyEventLoopPolicy())
定义时不报,set_event_loop_policy 一行报。函数侧的实现方式是内部保留下划线版本,公开名只负责发警告再调用:set_event_loop → _set_event_loop,set_event_loop_policy → _set_event_loop_policy。cpython 自己的测试也是这么改的:tearDown 里 asyncio.set_event_loop_policy(None) 换成 asyncio._set_event_loop_policy(None),断言改成 assertWarnsRegex(DeprecationWarning, "'set_event_loop_policy' is deprecated")。
依赖侧:pytest-asyncio 这类库还在用 policy
issue 讨论里明确提到 pytest-asyncio 这类库大量使用 policy,有人主张留 5 个 release(约 2 年)的弃用期,最终仍按 3.14 弃用、3.16 移除推进。弃用期内你的处理方式:
- 升级相关库到已适配
loop_factory的版本,这是首选; - 暂时升不上去,就接受这些警告存在,别在库内部用
warnings.catch_warnings()或全局filterwarnings把DeprecationWarning吞掉——用户自己的代码里可能也有同类用法,吞掉之后等到 3.16 直接变AttributeError; - 真要压制,压在自己的测试配置里,按模块定位,而不是在库的 import 路径上无条件忽略。
附带:asyncio.iscoroutinefunction 也进了名单
同一个 pending-removal-in-3.16.rst 里还有一条:asyncio.iscoroutinefunction 弃用,3.16 移除,改用 inspect.iscoroutinefunction(gh-122875,Jiahao Li 与 Kumar Aditya)。检查清单里一并搜掉。
检查清单
- 全局搜
set_event_loop_policy、get_event_loop_policy、AbstractEventLoopPolicy、DefaultEventLoopPolicy、WindowsSelectorEventLoopPolicy、WindowsProactorEventLoopPolicy、set_event_loop。 - 入口改成
asyncio.run(main());需要特定 loop 实现就加loop_factory=...,或用asyncio.Runner(loop_factory=...)。 - 宿主代码里
policy.new_event_loop()换成直接实例化 loop 类。 - Windows 上原本靠
WindowsSelectorEventLoopPolicy切 selector 的,改成loop_factory=asyncio.SelectorEventLoop。 - 子类化 policy 的代码直接删除,改为在构造 loop 的地方指定类。
- 回调与 coroutine 里的
get_event_loop()换成get_running_loop()。 asyncio.iscoroutinefunction换成inspect.iscoroutinefunction。- 用
-W error::DeprecationWarning跑一遍测试,确认剩下的警告都来自第三方库而不是自己。
参考:asyncio-policy 文档、asyncio-eventloop 文档、cpython issue #127949、pending-removal-in-3.16.rst。