ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零到一:用 `Plugins.register` 手动注册 Hydra 插件(官方示例深度解读)

从零到一:用 `Plugins.register` 手动注册 Hydra 插件(官方示例深度解读) 从零到一用Plugins.register手动注册 Hydra 插件官方示例深度解读【免费下载链接】hydraHydra is a framework for elegantly configuring complex applications项目地址: https://gitcode.com/GitHub_Trending/hyd/hydraHydra 是一个用于优雅配置复杂应用的框架其插件系统允许第三方扩展启动器Launcher、采样器Sweeper、配置源ConfigSource等能力。绝大多数第三方插件通过放入hydra_plugins包自动被发现但在某些场景下如插件不适合被打包安装、需要运行时条件注册你还可以走一条手动注册的捷径调用Plugins.instance().register(MyPlugin)在调用hydra.main之前把插件类直接挂载到 Hydra 的插件注册表中。本篇指南以仓库中的 example_registered_plugin 示例 为骨架结合 Plugins 单例源码 与核心测试用例讲清楚手动注册的完整步骤、底层机制、合法性校验规则及其与自动发现的差异读完你可以立即在自己的 Hydra 应用中复刻这一注册模式。为什么需要手动注册从自动发现说起Hydra 的插件体系以**自动发现discovery**为主。在Plugins单例初始化时_initialize它会扫描两个顶层模块hydra._internal.core_plugins—— Hydra 自带的核心插件基础启动器、基础采样器、各 Shell 补全插件等hydra_plugins—— 所有已安装的第三方插件包统一挂载的命名空间包。扫描使用pkgutil.walk_packages递归遍历上述模块下的所有子模块对每个模块执行inspect.getmembers凡是通过_is_concrete_plugin_type判定issubclass(obj, Plugin)且非抽象类的类都会被自动收集并注册。这正是仓库中 example_generic_plugin 示例所演示的路径——把插件源码放进hydra_plugins/目录并安装即可被自动发现。而Plugins.register提供的是完全平行的第二条路径不依赖目录扫描由你显式地把插件类塞进同一张注册表。官方示例 example_registered_plugin 的 README 直言这个插件没什么实际用途但它演示了如何通过Plugins.register方法注册插件——它存在的全部意义就是充当这份最小可运行的教学模板。手动注册的适用场景从注册表的结构可以推断手动注册在以下情形尤为合适插件类与主应用代码同源不希望拆成独立安装包插件仅在特定运行条件下才需要激活如仅当某个环境变量存在时在测试中临时注入自定义插件避免污染全局安装环境。最小示例解剖一个 16 行的注册插件整个示例插件包只有两个源文件我们先看核心实现 example_registered_plugin/init.pyfrom hydra.core.plugins import Plugins from hydra.plugins.plugin import Plugin class ExampleRegisteredPlugin(Plugin): def __init__(self, v: int) - None: self.v v def add(self, x: int) - int: return self.v x def register_example_plugin() - None: The Hydra user should call this function before invoking hydra.main Plugins.instance().register(ExampleRegisteredPlugin)关键点逐一拆解插件基类Plugin定义在 hydra/plugins/plugin.py是一个空的abc.ABC抽象基类。它是所有 Hydra 插件的共同祖先也是Plugins.register合法性校验的判据。示例插件直接继承Plugin而非Launcher/Sweeper等具名子类因此它只承担演示用途不参与任何实际功能的调度。普通方法即插件 APIExampleRegisteredPlugin(10).add(20)返回 30说明注册插件可以是携带任意业务逻辑的普通 Python 类手动注册并不要求实现特定接口。注册入口register_example_plugin()封装了对Plugins.instance().register(ExampleRegisteredPlugin)的调用。注释点明了使用时机——必须在调用hydra.main之前执行因为 Hydra 在解析配置、实例化 Launcher/Sweeper 时已经查询插件注册表。一次注册、两次发现的测试验证配套测试 tests/test_example_registered_plugin.py 用断言验证了注册前不可见、注册后可见的完整行为def test_discovery() - None: plugin_name ExampleRegisteredPlugin.__name__ # 注册前自动发现列表中不存在该插件 assert plugin_name not in [x.__name__ for x in Plugins.instance().discover(Plugin)] register_example_plugin() # 注册后出现在 discover(Plugin) 的结果中 assert plugin_name in [x.__name__ for x in Plugins.instance().discover(Plugin)] def test_example_plugin() - None: a ExampleRegisteredPlugin(10) assert a.add(20) 30这个测试精确刻画了Plugins单例的两大核心 API均在 hydra/core/plugins.py 中实现discover(plugin_type)L246-L263按类型返回已注册插件类的列表plugin_type省略时等价于查询Plugin全量集合register(clazz)L75-L81手动把插件类加入注册表。手动注册的底层机制单例保证全局唯一Plugins使用Singleton元类见 hydra/core/singleton.py 中对Plugins的声明任何地方调用Plugins.instance()拿到的都是同一实例因此手动注册的效果对进程内所有模块立即可见。注册表本身是两份成员数据__init__plugin_type_to_subclass_list插件类型 - 该类型的子类列表用于按类型发现class_name_to_class模块名.类名 - 类对象用于按完整限定名反查类。register 的两步动作register的实现在校验通过后委托给_registerL83-L92后者做两件事遍历PLUGIN_TYPESPlugin、ConfigSource、CompletionPlugin、Launcher、Sweeper、SearchPathPlugin见 L26-L33凡是issubclass(clazz, plugin_type)的把类追加进对应类型的列表中以模块名.类名为键写入class_name_to_class并特别地如果注册的是ConfigSource子类还会同步注册进SourcesRegistry.instance()——即注册一个配置源插件其效果等价于自动发现路径。因为_register会遍历全部六种插件类型并逐一判断继承关系所以一次register调用就能把插件登记到它继承的所有插件类型名下——这在核心测试 test_register_plugin 中有直接验证注册一个SearchPathPlugin子类后它同时出现在discover(Plugin)和discover(SearchPathPlugin)的结果中而不会出现在discover(Launcher)中。非法注册会被拒绝register第一步是合法性校验if not _is_concrete_plugin_type(clazz): raise ValueError(Not a valid Hydra Plugin)其中_is_concrete_plugin_type要求三个条件同时成立inspect.isclass是类、issubclass(obj, Plugin)继承自Plugin、not inspect.isabstract非抽象类。对应测试 test_register_bad_plugin 演示了注册一个普通类会抛出ValueError: Not a valid Hydra Plugin。注册不等于可实例化调度需要澄清一个边界register只把类登记进注册表它并不会立即创建实例。Launcher、Sweeper 的实例化发生在配置解析之后经由instantiate_launcher/instantiate_sweeperL133-L165调用_instantiate完成。_instantiateL94-L125有一个值得注意的限制——它要求插件类必须位于hydra_plugins.或hydra._internal.core_plugins.这两个白名单顶层包下见is_in_toplevel_plugins_module。换句话说手动注册能让类被发现discover但如果它是Launcher/Sweeper这类可被配置调度的插件其实例化仍然要求类位于hydra_plugins包内。示例插件只继承Plugin、不参与调度因此不受此约束这也是官方把它作为纯注册演示的原因。自动发现 vs 手动注册一张对照表维度自动发现自动路径Plugins.register手动路径触发方式Plugins单例初始化时扫描hydra_plugins包应用代码显式调用Plugins.instance().register(MyPlugin)插件存放位置hydra_plugins命名空间包常随pip install安装无限制可在应用源码任意位置定义注册时机应用启动、首次访问Plugins单例时需在调用hydra.main之前执行代表性示例example_generic_pluginexample_registered_plugin适用场景发布为可复用第三方包与应用同源、条件注册、测试注入两条路径最终汇入同一张注册表_register因此后续的discover、_instantiate行为完全一致。如何运行与验证这个示例示例插件包可独立安装运行。先看打包配置 setup.py包名hydra-example-registered-plugin安装依赖hydra-core注释提示生产环境可考虑锁定大版本如hydra-core1.1.*避免新主版本带来破坏性变更python_requires3.10分类器声明支持 Python 3.10 ~ 3.14、操作系统无关MANIFEST.in 通过recursive-include example_registered_plugin/* py.typed打包类型标记文件py.typed本身是空文件仅作为 PEP 561 类型声明。在仓库根目录执行hydra-core已作为依赖被安装cd examples/plugins/example_registered_plugin pip install -e . pytest tests/测试结果应当两个用例全部通过test_discovery证明register前后插件在discover(Plugin)结果中的出现与消失test_example_plugin证明注册的类可正常实例化并调用业务方法。注意pytest运行的前提是hydra-core已安装在当前 Python 环境。如果你只是想观察行为而不安装也可以把examples/plugins/example_registered_plugin目录加入PYTHONPATH后直接运行上述测试文件。实战模板如何在你的应用中手动注册插件把官方示例改造成一个可复用的实战骨架仅需四步第一步定义插件类任意位置继承Plugin或具体插件基类# myapp/myplugin.py from hydra.plugins.plugin import Plugin class MySearchPathPlugin(Plugin): 示例一个会往搜索路径追加自定义目录的插件 def __init__(self, extra_dir: str conf_extra) - None: self.extra_dir extra_dir第二步封装注册函数保持与官方一致的命名与时机约定def register_my_plugin() - None: 必须在 hydra.main 之前调用 from hydra.core.plugins import Plugins Plugins.instance().register(MySearchPathPlugin)第三步在入口调用# myapp/main.py from myapp.myplugin import register_my_plugin register_my_plugin() # 先注册 hydra.main(version_baseNone, config_pathconf, config_nameconfig) def main(cfg: DictConfig) - None: ...第四步按需验证from hydra.core.plugins import Plugins from hydra.plugins.plugin import Plugin assert MySearchPathPlugin in Plugins.instance().discover(Plugin)注意两点限制注册必须在hydra.main装饰的函数被调用即 Hydra 配置解析开始之前完成如果你的插件属于Launcher/Sweeper等可配置调度的类型请确保其类定义位于hydra_plugins包内否则即使注册成功也无法被_instantiate实例化。小结Plugins.register是 Hydra 插件体系中与自动发现并行的手动通道。通过 example_registered_plugin 这个最小示例我们看到了它的完整使用范式继承Plugin、封装注册函数、在hydra.main之前调用Plugins.instance().register(...)结合 Plugins 源码则能理解它背后的单例注册表、类型分发_register按六类插件类型归类、合法性校验_is_concrete_plugin_type以及实例化时的顶层包白名单限制。对于需要与应用同源代码共存、按条件激活或测试注入的插件手动注册是一条轻量、确定且可随时验证的路径。【免费下载链接】hydraHydra is a framework for elegantly configuring complex applications项目地址: https://gitcode.com/GitHub_Trending/hyd/hydra创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表