巧用“替身攻击”解决子进程 ModuleNotFoundError: No module named ‘uvloop‘

GitHub - MagicStack/uvloop: Ultra fast asyncio event loop. · GitHub
GitHub - Vizonex/Winloop: An Alternative library for uvloop compatability with windows · GitHub


📝 巧用"替身攻击"解决子进程 ModuleNotFoundError: No module named 'uvloop'

场景背景

在 Windows 上开发异步应用时,我们通常会编译并安装 winloop(具体编译过程见这篇前置教程),并在主程序入口通过 winloop.install() 成功替换了原生的 asyncio 事件循环。

在 Windows 11 上从源码编译安装 Winloop (uvloop 高性能事件循环) 保姆级实战指南-CSDN博客

然而,当你运行像 SGLang 这样的多进程框架时,主进程虽然跑通了,但框架在后台启动的全新子进程却直接崩溃,抛出报错:

ModuleNotFoundError: No module named 'uvloop'

原因分析

子进程是一个相对纯净的独立 Python 环境,它并不知道你在主进程里做过 winloop 的替换。而许多底层框架源码中硬编码import uvloop。当子进程执行到这一行时,因为 Windows 上根本装不上原版 uvloop,自然就崩溃了。


解决方案:本地模块伪装(Module Aliasing)

既然框架非要 uvloop,那我们就利用 Python 的模块导入优先级(当前目录优先),给它造一个"假"的。完全不需要修改框架源码。

  1. 在你的项目根目录下(即运行脚本的当前目录),新建一个名为 uvloop 的文件夹。

  2. 在该文件夹中新建一个 __init__.py 文件。

  3. 在文件中写入以下三行代码并保存:

Python

复制代码
import sys
import winloop

# 劫持当前模块,将其替换为 winloop
sys.modules[__name__] = winloop

原理解析

当框架的子进程启动并尝试 import uvloop 时,Python 会优先扫描当前目录。它会进入我们创建的 uvloop 文件夹并执行 __init__.py。此时,我们的代码通过修改 sys.modules,顺水推舟地把 winloop 伪装成了 uvloop 递给了框架。由于两者 API 完全一致,框架会毫无察觉地使用 winloop 继续飞奔。