为何asyncio.get_event_loop()在普通Python正常,IPython中报错?
问题现象
普通Python控制台(符合预期)
在标准Python交互环境中调用asyncio.get_event_loop()时,尽管会抛出DeprecationWarning,但仍会自动创建并返回一个新的事件循环实例:
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import asyncio >>> asyncio.get_event_loop() <stdin>:1: DeprecationWarning: There is no current event loop <_UnixSelectorEventLoop running=False closed=False debug=False>
IPython环境(获取失败)
在IPython 8.21.0中调用相同方法时,会直接抛出RuntimeError,提示主线程无当前事件循环:
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] Type 'copyright', 'credits' or 'license' for more information IPython 8.21.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: import asyncio In [2]: asyncio.get_event_loop() <ipython-input-2-6908e23590ee>:1: DeprecationWarning: There is no current event loop asyncio.get_event_loop() --------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) Cell In[2], line 1 ----> 1 asyncio.get_event_loop() File ~/.pyenv/versions/3.10.6/lib/python3.10/asyncio/events.py:656, in BaseDefaultEventLoopPolicy.get_event_loop(self) 653 self.set_event_loop(self.new_event_loop()) 655 if self._local._loop is None: --> 656 raise RuntimeError('There is no current event loop in thread %r.' 657 % threading.current_thread().name) 659 return self._local._loop RuntimeError: There is no current event loop in thread 'MainThread'.
甚至显式创建并设置事件循环后,问题依然存在:
In [3]: loop = asyncio.set_event_loop(asyncio.new_event_loop()) In [4]: asyncio.get_event_loop() <ipython-input-4-6908e23590ee>:1: DeprecationWarning: There is no current event loop asyncio.get_event_loop() --------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) [...] RuntimeError: There is no current event loop in thread 'MainThread'.
环境复现情况
该问题仅在特定组合环境中出现:
| host OS | runs in Docker* | reproduced |
|---|---|---|
| Ubuntu | yes | yes** |
| Ubuntu | no | no |
| MacOS | yes | no |
| MacOS | no | no |
- 使用命令
docker -it python:3.10启动容器
** - 在2台不同机器上均复现
- 使用命令
原因分析
asyncio API的废弃行为差异:
asyncio.get_event_loop()在Python 3.7+已被标记为废弃,官方推荐使用asyncio.get_running_loop()(获取当前运行的循环)或asyncio.new_event_loop()(创建新循环)。但标准Python控制台保留了兼容逻辑:当无当前循环时,会自动创建并绑定一个新循环到主线程。IPython的事件循环策略修改:
IPython为了支持交互式异步编程,会修改asyncio的默认事件循环策略(比如使用IPython.core.asyncio.AsyncIOPolicy)。在Docker环境的Ubuntu系统中,该自定义策略可能移除了"无当前循环时自动创建"的兼容逻辑,导致直接抛出RuntimeError。环境依赖差异:
Docker容器中的Ubuntu环境可能缺少某些系统依赖或环境变量,导致IPython的事件循环初始化逻辑异常,无法正确绑定默认循环到主线程。而MacOS或非Docker环境中,系统级的事件循环支持(比如kqueue)更完善,IPython的策略初始化能正常工作。显式设置无效的原因:
如果IPython替换了默认的事件循环策略,asyncio.set_event_loop()仅对当前策略生效,但如果该策略的get_event_loop()实现不认可手动设置的循环(比如要求循环必须由IPython自身管理),就会导致设置后依然无法获取到循环。
临时解决方案
- 直接使用
asyncio.new_event_loop()创建循环并手动管理:loop = asyncio.new_event_loop() # 后续使用loop执行异步任务 - 在IPython中使用
%autoawait魔法命令确保异步环境正常初始化:%autoawait on import asyncio loop = asyncio.get_running_loop() - 降级IPython版本到8.20.x或更早版本,部分用户反馈该版本在Docker Ubuntu环境中无此问题。
内容的提问来源于stack exchange,提问作者ffriend

