stevedore加载插件失败的根本原因是插件未正确安装或入口点未注册:插件须为合法包、父目录在sys.path中,且需通过pip install -e .安装并确保pyproject.toml中entry-points配置正确。

stevedore 如何加载插件模块而不报 RuntimeError: No module named
根本原因是 stevedore 默认只从 sys.path 里找包,不会自动把当前目录或自定义路径加进去。你本地写的插件模块如果没安装(即没走 pip install -e .),它就压根看不见。
实操建议:
立即学习“Python免费学习笔记(深入)”;
Delphi 7应用编程150例 CHM全书内容下载,全书主要通过150个实例,全面、深入地介绍了用Delphi 7开发应用程序的常用方法和技巧,主要讲解了用Delphi 7进行界面效果处理、图像处理、图形与多媒体开发、系统功能控制、文件处理、网络与数据库开发,以及组件应用等内容。这些实例简单实用、典型性强、功能突出,很多实例使用的技术稍加扩展可以解决同类问题。使用本书最好的方法是通过学习掌握实例中的技术或技巧,然后使用这些技术尝试实现更复杂的功能并应用到更多方面。本书主要针对具有一定Delphi基础知识
- 插件模块必须以合法 Python 包形式存在:含
__init__.py,且父目录在sys.path中;推荐用pathlib.Path(__file__).parent.parent动态加入路径 - 避免直接改
sys.path.append('./plugins')—— 路径写死、跨平台易错,改用importlib.util.spec_from_file_location()手动导入单个文件更可控 - 检查入口点(entry point)是否注册正确:不是在代码里写死,而是在
setup.py或pyproject.toml的[project.entry-points."my.plugin.group"]下声明
为什么 ExtensionManager 找不到已注册的入口点
常见现象是调用 manager.map() 后返回空列表,或初始化时报 No matching plugin found。这不是 stevedore 的 bug,而是入口点未被 Python 的 importlib.metadata(或旧版 pkg_resources)发现。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 确认插件包已通过
pip install -e .安装(开发模式),不能只靠python myapp.py就指望它自动加载 - 运行
python -m pip list | grep your-plugin-name看是否真在已安装包列表中;没出现说明注册失败 - 用
python -m importlib.metadata entry_points --group=my.plugin.group(Python ≥3.10)验证入口点是否被识别;若无输出,问题出在打包配置 - PyPI 包名和
setup.py中的name=必须一致,大小写敏感,且不能含下划线(PEP 508 规定仅允许 ASCII 字母、数字、连字符)
DriverManager 和 NamedExtensionManager 选哪个
二者核心区别不在功能强弱,而在“如何定位插件”:前者靠名字字符串匹配驱动名(如 'json'),后者靠显式传入名称列表做白名单过滤。选错会导致加载逻辑僵硬或漏加载。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 用
DriverManager当你需要类似driver = DriverManager('serializer', 'json')这种按需取一个的场景;它内部会查所有入口点,挑第一个匹配name的 - 用
NamedExtensionManager当你要限定只加载某几个明确命名的插件(比如只允许['yaml', 'toml']),避免用户传入非法名导致静默失败 - 注意
DriverManager不校验插件是否存在 —— 传入不存在的名字只会返回空扩展对象,不报错;而NamedExtensionManager遇到未注册名会直接抛RuntimeError - 性能上差别极小,但
NamedExtensionManager初始化时就得扫描全部入口点,而DriverManager是惰性查找,首次map()或map_method()才触发
插件函数返回值被包装成 Extension 对象,怎么拿到原始结果
stevedore 把每个插件封装成 Extension 实例,里面存着 .plugin(函数对象)、.name(名字)、.entry_point(入口点信息)。新手常误以为 manager.map(lambda ext: ext()) 就能执行,其实 ext 不是函数,不能直接调。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 正确调用方式是
ext.plugin(*args, **kwargs),不是ext(*args, **kwargs) - 用
manager.map(lambda ext: ext.plugin(data))比较直观;如果想保留插件名上下文,用manager.items()得到(name, ext)元组 - 别在插件函数里抛未捕获异常 ——
map()遇到任一插件崩溃就会中断整个遍历;改用manager.map_method('handle', data)并确保插件方法有基础异常兜底 - 如果插件函数需要访问配置或上下文,不要依赖全局变量;通过
manager初始化时传入invoke_on_load=True+invoke_args参数透传
pyproject.toml 却忘了重装,或者用了 venv 没激活,这些比 API 用法更容易让人花半天时间来回试。









