使用importlib.metadata时ReadTheDocs构建Sphinx文档失败原因排查
核心原因
importlib.metadata是从已安装到当前环境的包的元数据文件中读取版本,而非直接读取源码内容。切换到setuptools_scm后,你的构建流程依赖构建阶段生成的版本元数据,但ReadTheDocs的默认构建逻辑中,运行Sphinx生成文档时,包可能还未被正确安装到构建环境里,导致importlib.metadata找不到对应的包元数据。
之前用pbr时,pbr会在源码目录生成VERSION这类本地文件,或者RTD的构建流程会自动适配pbr的版本生成逻辑,直接导入包就能拿到__version__;但改用setuptools_scm后,若未提前触发包的安装或源码版本生成,就会出现找不到元数据的错误。
解决办法
1. 让conf.py直接从源码获取版本(推荐)
修改conf.py的版本获取逻辑,用setuptools_scm直接从源码仓库读取版本,无需依赖已安装的包:
try: # 直接从源码通过setuptools_scm获取版本 from setuptools_scm import get_version version = get_version(root="..", relative_to=__file__) except (ImportError, LookupError): # 降级到importlib.metadata(适用于已安装包的场景) try: from importlib.metadata import version as get_version version = get_version("pybliometrics") except ImportError: from importlib_metadata import version as get_version version = get_version("pybliometrics")
这样在RTD构建时,直接从源码仓库生成版本,绕过对已安装包的依赖。
2. 强制RTD先安装包再构建文档
调整.readthedocs.yaml的构建步骤,确保包先以可编辑模式安装(触发setuptools_scm生成元数据):
build: os: ubuntu-22.04 tools: python: "3.10" steps: - install: - pip install -e .[docs] # 安装包(含文档依赖) - sphinx_build: configuration: docs/conf.py
安装完成后,importlib.metadata就能从已安装的包中读取到版本元数据。
3. 让setuptools_scm自动填充__init__.py的版本
在pyproject.toml中配置setuptools_scm,让它自动把版本写入你的包的__init__.py:
[tool.setuptools_scm] write_to = "pybliometrics/__init__.py"
之后setuptools_scm会在构建/安装时自动在__init__.py中生成__version__变量,conf.py直接导入from pybliometrics import __version__即可,包代码也能直接使用这个变量,同时兼容源码和安装后的场景。
内容的提问来源于stack exchange,提问作者MERose

