Python setup.py导入包版本引发pip安装依赖缺失报错
问题根因
pip安装源码分发包的执行顺序是固定的:
- 解压tar.gz包,先执行
setup.py收集包元数据(也就是报错日志里的egg_info阶段) - 解析
setup.py里install_requires声明的依赖,下载并安装这些依赖 - 再执行实际的包安装逻辑
报错本质原因是:元数据收集阶段发生在依赖安装之前。你在setup.py中写from mypackage.version import VERSION时,Python的包导入机制会先执行mypackage/__init__.py,而这个文件里直接导入了依赖第三方库的some_code模块,some_code.py顶层又导入了尚未安装的jsonpickle、pyzmq,自然触发ModuleNotFoundError。
你本来只是想读取版本常量,结果连带触发了整个包的初始化逻辑,把还没安装的依赖提前加载了。
修复方案
按推荐优先级从高到低排列:
- 方案1:直接解析
version.py文件读取版本号,完全不触发包导入
这是Python打包的通用最佳实践,完全绕开包的初始化逻辑,不会有任何依赖导入问题。把setup.py中原来导入VERSION的代码替换为如下内容即可:import os import re ROOT_DIR = os.path.dirname(__file__) VERSION_FILE = os.path.join(ROOT_DIR, "mypackage", "version.py") with open(VERSION_FILE, encoding="utf-8") as f: # 正则匹配VERSION常量的赋值 VERSION = re.search( r'^VERSION\s*=\s*["\']([^"\']+)["\']', f.read(), re.MULTILINE ).group(1) - 方案2:直接加载version模块,跳过包的
__init__.py
如果不想用正则解析,可以用importlib直接加载单独的version.py文件,不会触发mypackage包的初始化流程,注意要保证version.py本身只存版本常量,不导入任何依赖第三方库的模块:import os import importlib.util ROOT_DIR = os.path.dirname(__file__) VERSION_FILE = os.path.join(ROOT_DIR, "mypackage", "version.py") spec = importlib.util.spec_from_file_location("pkg_version", VERSION_FILE) version_mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(version_mod) VERSION = version_mod.VERSION - 方案3:调整包初始化逻辑(不推荐)
可以修改__init__.py,不在顶层导入依赖第三方库的业务模块,改成懒加载或者按需导入,但这种方式治标不治本,后续修改业务代码时很容易再次引入同类问题。
核心原则:
setup.py本身的执行不能依赖install_requires里声明的任何第三方包,也尽量不要导入自己包内带业务逻辑的模块,避免触发不必要的依赖加载。
内容的提问来源于stack exchange,提问作者Wör Du Schnaffzig
相关产品推荐
相关产品推荐

