ModuleNotFoundError: No module named 'orjson'这个报错,本地开发、生产服务器、CI 构建环境里我都踩过。先说结论:它基本不是代码 bug,而是安装链路出了问题。orjson 是一个用 Rust 写的高性能 JSON 解析库,很多现代 Python 库(FastAPI、Pydantic 等)在部分场景会把它当依赖带进来,所以你很可能根本没直接安装过它,却在跑项目时被它卡住。这篇文章我会从 orjson 为什么容易安装失败讲起,把报错背后的 wheel、源码编译、Python 版本这些知识拆开说透,然后给一套能直接照抄的解决流程,最后把排查过的各种奇怪报错整理成速查表。新手可以顺着读,老手可以直接跳到第 3 章的救命命令。
1. 先搞明白 orjson 为什么这么“矫情”
1.1 orjson 到底是个什么东西
orjson 是一个专注于 JSON 序列化和反序列化的第三方库,特点是快。它对json.dumps和json.loads的场景做了大量底层优化,在某些数据量大的项目里,性能能比标准库快好几倍。因为这个性能优势,很多做 Web 接口、异步任务、数据处理的项目会在自己的依赖里直接或间接使用它。
有意思的是,orjson 不是用 Python 写的,主体是一个 Rust 扩展模块。这也是它后续一系列安装问题的根源。当你 pip 安装它时,正常情况下会拿到一个预编译好的二进制包,直接解压就能用;但如果拿不到预编译包,pip 就会尝试从源码包现场编译一个扩展模块出来。编一个 Rust 扩展,就需要你的机器上有一套完整的 Rust 编译工具链,以及对应平台的 C/C++ 编译环境。这两样缺任何一样,安装过程就会在中途失败,运行项目时自然报No module named 'orjson'。
所以我的排查思路一向是:不要只盯着“模块没装上”这个表象,而要去看安装阶段发生了什么。是包根本找不到?是网络没拉下来?是编译工具缺失?还是装到了另一个 Python 环境?每种情况的表现都像,处理方式完全不一样。
1.2 报错的本质:wheel 与源码构建
要理解 orjson 为什么会出现这种问题,得先搞懂 pip 下载包时的选择逻辑。PyPI 上每一个 Python 包可以同时提供多种格式的分发包,最常见的是两种:
- wheel:预编译的二进制包,里面已经是编译好的可执行模块,pip 下载后解压就能用,不需要编译。
- sdist:源码包,pip 下载后需要在本机执行编译/构建流程,生成可导入的扩展模块。
pip 在安装一个包时,会优先选择符合条件的 wheel。所谓“符合条件”,要看很多标签:当前操作系统、CPU 架构、Python 主版本和次版本、pip 本身的版本算法支持范围。只要这些标签匹配不上,pip 就会退回到 sdist 去编译。
orjson 的特殊之处在于,它用了 Rust,所以 sdist 编译时需要cargo。你在安装日志里看到Building wheel for orjson (pyproject.toml)然后长时间卡住,之后突然抛出error: can not find Rust compiler,就是掉进了 sdist 编译这条路。
给新手打个比方:wheel 相当于你在家具城买了一把组装好的椅子,拆开快递就能坐;sdist 相当于卖家给你发了一包木板和螺丝,你需要自己准备电钻、螺丝刀,还得看得懂说明书才能拼起来。你的电钻没带,椅子自然坐不上。
这也解释了一个反直觉的现象:为什么其他库 pip install 一秒装好,orjson 却一堆事。因为它从“预组装家具”变成“散装木板”的概率,比其他纯 Python 库高得多。
2. 动手前的体检:5 分钟摸清环境状态
2.1 检查 Python 版本与 pip 状态
在解决依赖问题前,先确认三件事:当前用的 Python 是哪个版本、pip 是不是绑定了同一个解释器、orjson 到底装没装过。不是你敲一个pip install orjson就完事的,很多问题都出在“装的时候装到了别的解释器里”。
先跑这三条命令:
python --version python -m pip --version where python注意我这里用的是python -m pip,而不是裸pip。原因很简单:如果你的机器上有多个 Python(比如系统自带一个 3.9、Anaconda 装了一个 3.11、某软件又塞了一个 3.8),你在终端里敲pip时,Windows 会按 PATH 顺序找一个 pip.exe,找到的不一定是你python命令对应的那一个。用python -m pip能保证你操作的 pip 一定是当前这个 python 解释器自带的模块,避免环境错乱。
where python(Linux/macOS 用which python)可以帮你看到当前终端实际会调用哪个 Python。如果你想确认是不是装到了别的环境,可以再跑一个:
python -c "import sys; print(sys.executable)"这个命令打印的是当前解释器的绝对路径。记住这个路径,后面排查时它就是“裁判”。
2.2 确认 orjson 是否已有残存安装
检查 orjson 现有的安装状态:
python -m pip show orjson python -c "import orjson; print(orjson.__version__)"这两条命令的结果有几个组合,对应的处理方式不同:
pip show没有输出,import报错:说明 orjson 根本没装,直接进入下一章正题。pip show有输出,但import orjson失败:最常见的解释是安装被中断、文件损坏,或者你之前手动把 site-packages 里的文件改坏了。处理方式也很简单,强制重装一遍:
python -m pip install --force-reinstall --no-deps orjsonpip show有输出,import成功,但在你的项目里仍然报No module named 'orjson':这种情况几乎可以断定是项目解释器不是刚才这个 Python。检查 IDE 里项目解释器选的是否sys.executable打出来的路径。PyCharm、VS Code 坑过很多人,项目里 A 解释器、终端却是 B 解释器,装哪儿都白搭。
2.3 顺手把 pip 和 setuptools 升级到位
升级 pip 这个动作,很多人会忽略,但它在 orjson 这类问题上特别关键。pip 对 wheel 的支持能力不是恒定的,老版本的 pip 不认识新的 wheel 标签格式。比如现在的 Linux wheel 经常用manylinux_2_17_x86_64这类标签,如果你的 pip 还停留在 18/19 版本,它可能根本不知道这个 wheel 适用于自己的平台,于是宁可放弃 wheel 走源码编译,或者直接报No matching distribution found。
建议在装任何包之前,先统一升级构建链路:
python -m pip install --upgrade pip setuptools wheel注意 Windows 上如果使用 PowerShell,可能遇到一个经典报错:无法加载文件 ...Activate.ps1,因为在此系统上禁止运行脚本。这是 PowerShell 的执行策略拦住了脚本。临时解决方式是用管理员身份运行一次:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser或者干脆不用激活脚本,直接把命令通过python -m方式执行,绕开脚本执行策略也一样能装包。不要因为这个问题卡在门口。
3. 核心解法:照着抄就能装上网线
3.1 直通车:先用预编译 wheel 装一次
如果 orjson 当前没有安装,且你的 Python 版本不是太古董,第一条命令就足够解决问题:
python -m pip install orjson正常情况下,pip 会从 PyPI 拉取一个匹配当前平台的 wheel 文件并安静装上。装完再验证一下:
python -c "import orjson; print(orjson.__version__)"如果输出类似3.9.15,说明已经成功,问题结束。
但如果你在国内网络环境下面装,可能会频繁遇到超时、连接断开、下载到一半失败。这时候直接换镜像源,我个人用的是清华的 PyPI 镜像:
python -m pip install orjson -i https://pypi.tuna.tsinghua.edu.cn/simple如果还想更稳,可以给 pip 追加一个超时参数:
python -m pip install --timeout 60 orjson -i https://pypi.tuna.tsinghua.edu.cn/simple还有一个非常有用的参数,很多老手都在用:--only-binary=:all:。它的意思是不管怎么装,都必须用 wheel 形式的包,绝不允许 pip 退回源码编译。这么做的好处是,如果系统根本没有可用的 wheel,pip 会立刻报错告诉你,而不会傻乎乎地在那编译半天最后缺 Rust 再报错。
python -m pip install --only-binary=:all: orjson如果这条命令成功,恭喜你,你已经彻底绕过了编译地狱。如果这条命令直接报Could not find a version that satisfies the requirement orjson,说明在当前平台和 Python 版本下确实没有预编译 wheel,你需要看下一节,锁定一个旧版本再试。
3.2 老版本 Python 锁定兼容版本
orjson 版本迭代很快,新版本通常会放弃对老 Python 的支持。比如较新的 orjson 要求 Python 3.8+,如果你的环境还是 Python 3.7,直接装最新版大概率找不到可用的 wheel,于是掉进源码编译。
先看看当前环境中哪些 orjson 版本可用。pip 21.2 以上可以用:
python -m pip index versions orjson老版本 pip 不支持这个命令,可以用一个取巧方式,故意指定一个不存在的版本号,pip 会列出所有可用版本供你参考:
python -m pip install orjson==拿到版本列表后,按当前 Python 版本挑选兼容的。一个大致的对应关系表(以官方发布说明为准):
| Python 版本 | 建议 orjson 版本 |
|---|---|
| Python 3.7 | orjson 3.9.x 及以下 |
| Python 3.8 | orjson 3.10.x 或按需低版本 |
| Python 3.9 / 3.10 / 3.11 / 3.12 | 最新版一般均支持 |
如果你不确定,最保守的方案是装一个覆盖面很广的版本:
python -m pip install "orjson<3.10"我用这个方式解决过不少老服务器的部署问题。Python 3.8 环境下,锁到3.9.15基本都能顺利找到 wheel。如果你在维护一个 requirements.txt,也可以直接在文件里写:
orjson>=3.9,<3.10这样重建环境时就不会因为拉取最新版而翻车。
这里多说一句:不要迷信“最新版一定最好”。很多生产事故就是升级依赖到最新版触发的不兼容。在没有明确功能需求的情况下,锁定一个长期稳定的次新版本是更务实的做法。
3.3 没有 wheel 时的编译安装方案
在某些场景下你可能确实找不到现成 wheel:小众 CPU 架构(比如老 ARM 板子)、特别古老的 Python 分支、或者平台组合太冷门。如果你坚持要自己编译安装,需要准备编译工具链。orjson 是 Rust 扩展,所以 Rust 工具链是必须的,另外还需要 C/C++ 编译器。
Windows 上的操作步骤:
- 安装 Visual Studio Build Tools,勾选“使用 C++ 的桌面开发”工作负载。
- 安装 Rust。到官网下载 rustup-init.exe,运行后按提示安装。国内网络建议配置镜像以加速,这里不展开,记住一点:安装完成后重启终端,执行
cargo --version确认可用。
Linux 上的操作步骤:
sudo apt update sudo apt install python3-dev build-essential curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source "$HOME/.cargo/env"macOS 上的操作步骤:
xcode-select --install curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh编译工具都到位后,再执行python -m pip install orjson,pip 会从 sdist 现场构建,过程可能要几分钟,最终如果能看到Successfully built orjson就说明编译成功。
但我得泼一盆冷水:为 orjson 单独装一整套 Rust 工具链,其实非常不划算。如果你只是想跑通项目,更省事的方案是换一个官方支持更高 Python 版本的运行时,或者直接用 Docker 镜像。Docker 的好处是镜像里通常已经有编译好的依赖,装 orjson 往往一条命令就过,完全不用折腾本机环境。编译安装是我最后的手段,优先级最低。
4. 高频报错与排查实录
4.1 报错对照速查表
把我在各种环境里实际遇到过的报错整理成一张表,你可以按图索骥:
| 报错现象 | 可能原因 | 直接解决办法 |
|---|---|---|
ModuleNotFoundError: No module named 'orjson' | orjson 未安装,或装到了别的解释器 | 用python -m pip install orjson安装;检查解释器路径是否一致 |
安装时报error: can not find Rust compiler | pip 退回源码构建,但缺 Rust | 优先用--only-binary=:all:强制 wheel;确认无 wheel 才装 Rust |
安装时报Could not find a version that satisfies the requirement | Python 版本过老/过新、网络源没有该版本 | 锁定兼容版本;换镜像源;用pip debug --verbose查看 wheel 标签 |
安装时说Requirement already satisfied,但 import 仍报错 | 安装到了另一个环境 | where python/which python查看实际解释器,统一用python -m pip |
Building wheel for orjson卡住很久后失败 | 源码构建环境缺依赖 | 取消自动构建,用 wheel;换 Python 版本 |
| Windows 编译报错缺少 MSVC C++ 编译器 | 源码构建需要 VS Build Tools | 避免源码构建;或安装 Build Tools 后重试 |
提示You must give at least one requirement to install (see "pip help install") | pip install 后没带包名 | 写成pip install 包名,千万别漏 |
| 安装时网络超时或连接被重置 | 网络到 PyPI 不稳定 | 换国内镜像源,加--timeout 60 |
这个表的核心逻辑就一句话:先判断 pip 是不是掉进了源码构建,再判断是不是环境错位,最后才是网络问题。很多人一报错就重装 Python,这是最没有必要的折腾。
4.2 虚拟环境里的经典坑
虚拟环境照理说是隔离依赖的,但隔离不了人犯的迷糊。我处理过几次典型的“虚拟环境里 orjson 装不上”的求助,最后发现根本不是虚拟环境的问题。
一种情况是,进到虚拟环境之后,用户敲的还是裸pip,而系统里 pip 命令指向的还是全局环境。结果包装到了全局 Python,虚拟环境里该报错还是报错。这种问题很好验证:
which pip虚拟环境激活后,which pip应该指向虚拟环境目录下的路径,比如.venv/bin/pip。如果它仍然指向/usr/bin/pip,说明你不是在虚拟环境里操作。解决方式要么重新激活,要么像我一直强调的那样,用python -m pip,因为这里的python才是虚拟环境里的解释器。
另一种情况是 conda 环境和 pip 混用。conda 创建的虚拟环境可以调用 pip 装包,但如果环境中 pip 版本太老,或者 conda 和 PyPI 的包版本冲突,也会出现安装成功、导入失败之类的诡异情况。我的经验是:先conda update pip,再用python -m pip install,避免直接用系统级 pip 去装。
还有 Windows 上激活虚拟环境时,PowerShell 的脚本执行策略可能阻止Activate.ps1运行。别慌,那不是 orjson 的问题,而是 PowerShell 安全策略。处理方法前面提过,设置一下执行策略再激活就好。最好先确认环境激活有效,再执行后续命令。
4.3 举一反三:缺失模块都这么查
orjson 只是“Python 缺失模块”这一大坑的典型代表。热词里经常出现的pkg_resources、opencv报错,核心思路完全一致。
比如ModuleNotFoundError: No module named 'pkg_resources',它是setuptools自带的模块,解决方式通常就是:
python -m pip install --upgrade setuptools再比如No module named 'opencv(准确来说是No module named 'cv2'),对应的包名应该是opencv-python,很多人对着opencv这个名字一顿装,怎么装都不对。这里就引出一个排查原则:你要装的包名不一定等于 import 的模块名。import cv2对应包名opencv-python,import orjson对应包名orjson,import yaml对应包名pyyaml。所以在报错时,先查 PyPI 上模块对应的发布名。
我建议遇到这类缺失模块问题,按“三查”来走:
- 查环境:当前解释器是谁,依赖装给了谁。
- 查渠道:有没有匹配的 wheel,源里有没有这个版本。
- 查版本:Python 版本和库版本是否在支持矩阵内。
如果依赖关系复杂,还可以用pipdeptree查看依赖树,快速找出是哪个包把 orjson 拉进来的:
python -m pip install pipdeptree python -m pipdeptree这个命令输出后,你能看到项目依赖的完整树状结构,定位到 orjson 是怎么被间接依赖的。有些时候,真正该做的是升级那个上游包,而不是单独修 orjson。
我在实际工作中养成了一个习惯:不管三七二十一,先跑pip debug --verbose看当前环境支持的 wheel 标签列表。这个命令输出很多信息,但关键点在Compatible tags这一段。如果 orjson 提供给当前平台的所有 wheel 都不在这份标签列表里,那么用--only-binary=:all:必然会失败,你也就知道下一步只能锁旧版本或换环境,省得瞎试。
回头再看 orjson 这个问题,本质上是个典型的依赖分发问题。越早理解 wheel 和 sdist 的区别,这类报错就越好处理。我的建议是先锁定一个兼容版本并强制走 wheel 通道,这一条命令能解决绝大多数本地环境问题和九成服务器部署问题。只有当你确实需要 orjson 的新功能,或者运行平台特别冷门时,才考虑拉 Rust 编译工具链这条路。按这个顺序走,你通常十分钟内就能把项目跑起来,而不是陷入“重装 Python—再报错—再重装”的循环。