You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在IPython启动文件中运行异步代码?

如何在IPython启动文件中运行异步代码?

首先得说清楚为什么你直接在启动文件里写await会报错——这和IPython的启动流程直接相关:

IPython的启动脚本是在交互式环境完全初始化之前运行的,这时候它还没开启「顶层await支持」。你在交互式shell里能直接写await foo(),是因为IPython会偷偷把你的输入代码包装成一个异步函数,然后用它自己管理的事件循环来执行;但启动文件的代码是直接在普通的同步Python上下文中跑的,就像你在普通.py脚本里直接写await一样,肯定会报「不在异步函数里」的语法错误。

而且你不想用asyncio.run()是完全正确的,因为它会创建一个独立的新事件循环,跑完就直接关掉,这样你初始化的Tortoise-ORM连接之类的上下文就带不到后续的交互式shell里了,完全达不到启动初始化的目的。

那怎么才能复用IPython后续要用来交互的那个事件循环,在启动阶段跑异步代码呢?给你两个靠谱的方案:

方案1:用IPython的run_cell直接执行带await的代码

IPython的run_cell方法会用和交互式shell完全一样的上下文处理代码,自动帮你搞定顶层await的问题,而且会复用同一个事件循环。直接在启动文件里这么写:

# .ipython/profile_default/startup/01_hello.py
print("hello!")
async def foo():
    print("foo")
# 这里可以加入你的异步初始化逻辑,比如Tortoise-ORM的初始化代码

# 获取当前IPython实例
from IPython import get_ipython
ip = get_ipython()

# 确保我们在IPython环境中(避免脚本单独运行时报错)
if ip is not None:
    # 用run_cell执行带await的代码,这会复用IPython的事件循环
    ip.run_cell("await foo()")
    # 如果是初始化Tortoise,就写 ip.run_cell("await init_tortoise()")

运行之后你会看到预期的效果:

$ ipython
Python 3.12.0 (main, Nov 12 2023, 10:40:37) [GCC 11.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.31.0 -- An enhanced Interactive Python. Type '?' for help.
hello
foo

In [1]:

这样异步代码跑完后,事件循环会保留下来,你后续在交互式shell里用Tortoise-ORM之类的工具时,初始化的上下文也能正常复用。

方案2:用IPython的启动钩子,等事件循环就绪再执行

如果你的异步初始化逻辑比较复杂,或者担心启动阶段IPython的事件循环还没完全准备好,可以用IPython的post_run_startup钩子——这个钩子会在所有启动脚本跑完、交互式环境彻底准备好之后触发,这时候事件循环已经完全就绪:

# .ipython/profile_default/startup/01_init.py
import asyncio
from IPython import get_ipython

async def async_init():
    print("Starting async initialization...")
    # 这里写你的异步初始化逻辑,比如Tortoise-ORM的连接
    # await Tortoise.init(...)
    await asyncio.sleep(0.2) # 模拟异步操作
    print("Async initialization done!")

# 获取IPython实例
ip = get_ipython()
if ip is not None:
    # 定义一个回调函数,当启动完成后执行
    def trigger_async_init(_):
        # 获取IPython正在使用的事件循环
        loop = asyncio.get_running_loop()
        # 把异步任务提交到这个循环里
        loop.create_task(async_init())
    
    # 注册钩子:post_run_startup 会在所有启动脚本执行完后触发
    ip.events.register("post_run_startup", trigger_async_init)

这个方案更稳妥,因为它确保你的异步代码是在IPython的事件循环完全启动后才执行的,上下文和后续的交互式shell完全一致。

总结一下:核心就是别在启动文件里直接写await,而是借助IPython自己的工具(run_cell或者启动钩子),让异步代码跑在它的事件循环上下文里,这样既不会报错,又能保留初始化的上下文。

备注:内容来源于stack exchange,提问作者odigity

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 12:59:33