应改用 importlib.metadata.entry_points() 容错加载插件,避免 pkg_resources;setup.py 中 entry_points 必须为字典格式,pyproject.toml 需声明 build-backend,否则被忽略。

pkg_resources.entry_points 已被标记为弃用,Python 3.8+ 推荐改用 importlib.metadata(或兼容包 importlib-metadata),硬依赖 pkg_resources 会导致构建失败、环境冲突和运行时警告。
entry_points 在 setup.py 中怎么写才不被 pip 拒绝
新版 pip(21.3+)默认禁用 setup.py install,且对 entry_points 格式更严格。必须使用字典形式声明,不能用字符串拼接或动态生成:
entry_points={
"myapp.plugins": [
"csv_loader = myapp.plugins.csv:CSVLoader",
"json_parser = myapp.plugins.json:JSONParser",
],
"console_scripts": [
"myapp-cli = myapp.cli:main",
],
}常见错误包括:
- 把
entry_points写成字符串(如entry_points = "myapp.plugins = ...")→ 安装时报Invalid field - 模块路径中含空格或非法字符 →
pkg_resources.DistributionNotFound - 未在
pyproject.toml中声明构建后端(如build-backend = "setuptools.build_meta")→entry_points被完全忽略
运行时如何安全加载插件而不崩溃
不要直接调用 pkg_resources.iter_entry_points("myapp.plugins") —— 它会在找不到分发包时抛 DistributionNotFound,且无法区分“未安装”和“损坏安装”。应封装容错逻辑:
立即学习“Python免费学习笔记(深入)”;
from importlib.metadata import entry_points
try:
eps = entry_points(group="myapp.plugins")
except TypeError: # Python < 3.10 兼容
eps = entry_points().get("myapp.plugins", [])
<p>for ep in eps:
try:
plugin = ep.load()
register_plugin(plugin)
except (ImportError, AttributeError) as e:</p><h1>忽略单个插件失败,不中断整个加载流程</h1><pre class='brush:python;toolbar:false;'> log.warning(f"skip plugin {ep.name}: {e}")</pre>关键点:
-
entry_points(group=...)返回EntryPoints对象(3.10+)或list(旧版),别假设返回类型 -
ep.load()可能触发模块导入异常,必须单独捕获 - 避免在插件加载阶段执行副作用代码(如初始化数据库连接),应延迟到
.load()后显式调用.init()
为什么插件模块 import 失败却没报错信息
典型现象:插件包已安装,entry_points() 能枚举出入口,但 ep.load() 静默失败或抛出模糊的 ModuleNotFoundError。根本原因常是:
- 插件包的
__init__.py或目标模块里有未满足的依赖(如用了requests但没声明在install_requires) - 插件模块路径包含相对导入(
from .utils import helper),而ep.load()是从顶层导入,导致SystemError: Parent module '' not loaded - 插件包用了
pyproject.toml但没配[project] requires-python,导致 Python 版本不匹配时仅静默跳过
调试建议:
- 手动运行
python -c "import myapp.plugins.csv"看是否报错 - 检查插件包的
dist-info/METADATA文件,确认Requires-Dist字段完整 - 用
pip show myapp-plugins-csv验证安装路径是否在sys.path中
entry_points 的核心价值不在“自动发现”,而在“解耦注册时机”——它把插件加载推迟到运行时,但这也意味着所有错误都变成运行时问题。务必在首次加载插件时做完整性校验(比如调用 plugin.validate()),而不是等到实际使用才暴露缺失依赖。


















