1. 概述
在使用 Docker Compose 构建 Python 项目镜像时,你可能会遇到如下错误:
AttributeError: cython_sources
这个错误通常出现在依赖项升级或 Cython 版本更新之后。虽然表面上看起来像是 Docker Compose 的问题,但其根本原因在于 Python 包之间的版本冲突,尤其是 PyYAML 和 Cython 之间的兼容性问题。
本文将带你分析这个错误的成因、为什么昨天还能构建的镜像今天却失败了,以及最实用的几种修复策略。最终目标是帮助你快速恢复构建流程,并避免类似问题在未来再次发生。
2. 理解错误信息
要解决这个问题,首先得理解 cython_sources
是什么,以及它为何会引发错误。
2.1. 什么是 cython_sources?
Cython 是一种将 Python 代码编译为 C 扩展的工具,用于提升性能。许多 Python 包(如 PyYAML)在安装时会调用 Cython 编译 .pyx
或 .pxd
文件生成 C 源码,然后进一步编译为二进制模块。
在 Cython 3.0 中,其内部 API 发生了重大变更。PyYAML 5.x 及更早版本依赖的 cython_sources
属性在新版本中被移除,导致其构建失败,从而抛出:
AttributeError: cython_sources
⚠️ 这个错误通常发生在从源码安装 PyYAML 时(例如在 Docker 构建过程中)。
2.2. 为什么在 Docker Compose 构建中出现?
Docker Compose 本身只是协调多个容器的工具,它并不直接导致错误。真正的问题是容器内的 Python 环境在安装依赖时,尝试使用新版本的 Cython 编译旧版本的 PyYAML。
举个例子,假设你的 requirements.txt
是这样的:
pyyaml==5.4.1
cython==3.0.0
再看一个简单的 Dockerfile
:
FROM python:3.9-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
CMD ["python", "--version"]
执行构建:
docker build -t pyyaml-cython-bug .
你会看到构建失败,日志中包含:
...
AttributeError: cython_sources
这就是因为 PyYAML 5.4.1 不兼容 Cython 3.0 所致。Docker 只是暴露了这个问题,而不是造成问题的根本原因。
3. 常见触发场景与表现
3.1. PyYAML 兼容性问题
PyYAML 因为使用了 Cython 加速,所以在安装时会调用 Cython 编译源码。如果你的依赖中包含 PyYAML,并且版本低于 6.0.1,同时又安装了 Cython ≥ 3.0,就很可能遇到构建失败。
一个典型的错误日志如下:
error: subprocess-exited-with-error
× Getting requirements to build wheel did not run successfully.
...
raise AttributeError(attr)
AttributeError: cython_sources
更麻烦的是,有时候你并没有显式安装 PyYAML,而是通过其他依赖(如 drf-spectacular
或 awscli
)间接引入的。
比如你的 requirements.txt
可能是这样的:
drf_spectacular>=0.26.2
awscli>=1.29.0
但在构建时,你会看到:
Collecting PyYAML>=5.1
Downloading PyYAML-5.4.1.tar.gz (115 kB)
Installing build dependencies: finished with status 'error'
...
AttributeError: cython_sources
3.2. 依赖版本未锁定的风险
Python 项目中常见的依赖管理方式有两种:
✅ 显式指定版本(推荐):
pyyaml==6.0.1
cython==0.29.36
drf-spectacular==0.26.2
❌ 使用浮动版本(有风险):
pyyaml>=5.4.0
cython>=0.29.0
drf-spectacular>=0.26.2
后者虽然方便,但也容易因依赖自动升级而导致构建失败。例如:
- PyYAML 升级到 5.4.1
- Cython 升级到 3.0.0
这两个版本组合就会触发 cython_sources
错误。
⚠️ 更严重的是,某些旧版本的 PyYAML(如 5.3.1)存在安全漏洞 CVE-2020-14343,允许远程代码执行。因此,依赖锁定虽然能解决构建问题,但也可能引入安全风险。
4. 实用解决方案
下面列出几种常见的修复策略,你可以根据项目实际情况选择最合适的方式。
4.1. 升级 PyYAML 到兼容版本
最直接的解决方法是将 PyYAML 升级到 6.0.1 或更高版本。这个版本已经适配了 Cython 3.0 的变化。
修改 requirements.txt
:
pyyaml==6.0.1
drf-spectacular==0.26.2
awscli==1.29.4
然后重新构建:
docker-compose build
如果某些依赖强制要求使用旧版 PyYAML,可以尝试将 pyyaml==6.0.1
放在 requirements.txt
的最上方,或使用 pip
的 --force-reinstall
参数覆盖安装。
4.2. 固定 Cython 版本为 < 3.0
如果你暂时无法升级 PyYAML,可以将 Cython 版本限制为 3.0 以下:
FROM python:3.9-alpine
WORKDIR /app
RUN pip install --no-cache-dir "cython<3.0.0"
COPY requirements.txt /app/
RUN pip install --no-cache-dir -r requirements.txt
COPY . /app
CMD ["python", "main.py"]
⚠️ 这种方式只是权宜之计。长期来看,还是建议升级依赖版本以获得更好的兼容性和安全性。
4.3. 使用 --no-build-isolation
参数
对于高级用户,可以尝试禁用 pip 的构建隔离,从而跳过某些构建逻辑:
pip install --no-build-isolation pyyaml==5.4.1
这种方式会将当前环境直接用于构建,适用于你已经手动安装好兼容版本的情况。
⚠️ 缺点也很明显:可能导致构建结果不稳定,且容易因环境变化而失败。建议仅在紧急修复生产环境时使用。
4.4. 升级间接依赖库
如果你的错误是由 drf-spectacular
或 awscli
引入的 PyYAML 导致的,可以尝试升级这些库:
awscli>=1.29.4
drf-spectacular>=0.29.0
查看这些库的官方文档或 GitHub Issues(如 AWS CLI issue #8036),通常会有对 Cython/PyYAML 兼容性的说明。
5. 总结
AttributeError: cython_sources
错误本质上是 Python 依赖版本冲突问题,尤其是在使用 Docker 构建时更容易暴露出来。
✅ 常用修复策略包括:
方法 | 适用场景 | 风险 |
---|---|---|
升级 PyYAML 到 6.0.1+ | 推荐首选方案 | 无 |
固定 Cython 版本为 < 3.0 | 紧急修复 | 不推荐长期使用 |
使用 --no-build-isolation |
临时调试/修复 | 环境依赖复杂 |
升级间接依赖库 | 多层依赖问题 | 需查文档 |
建议优先升级依赖版本以获得更好的兼容性和安全性。同时,建议在 requirements.txt
中使用显式版本号(pinned),避免未来因依赖漂移而再次踩坑。
✅ 记住:Docker Compose 只是暴露问题的工具,真正的根源是 Python 包之间的版本不兼容。