1. 概述

在使用 Docker Compose 构建 Python 项目镜像时,你可能会遇到如下错误:

AttributeError: cython_sources

这个错误通常出现在依赖项升级或 Cython 版本更新之后。虽然表面上看起来像是 Docker Compose 的问题,但其根本原因在于 Python 包之间的版本冲突,尤其是 PyYAMLCython 之间的兼容性问题。

本文将带你分析这个错误的成因、为什么昨天还能构建的镜像今天却失败了,以及最实用的几种修复策略。最终目标是帮助你快速恢复构建流程,并避免类似问题在未来再次发生。


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-spectacularawscli)间接引入的。

比如你的 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-spectacularawscli 引入的 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 包之间的版本不兼容。


原始标题:Troubleshooting ‘AttributeError: Cython Sources’ When Docker-Compose Fails to Build Images