假设我们有一个具有以下结构的Python项目:
hydra_config
├── conf
│ ├── api_key
│ │ ├── non_prod.yaml
│ │ └── prod.yaml
│ └── db
│ ├── mysql.yaml
│ └── postgresql.yaml
├── modules
│ └── module.py
└── my_app.py
现在,在 Hydra 的配置文档中,他们指出我们需要在想要访问配置文件的函数之上添加一个 Python 装饰器。但是,文档仅展示了如何对项目中的函数执行此操作my_app.py
这是项目的主要模块。
问题是,如何添加
@hydra.main(config_path="conf")
Python装饰器到一个函数,假设module_function
哪个位于modules/module.py
?以下是module.py
的内容:
import hydra
from omegaconf import DictConfig, OmegaConf
@hydra.main(config_path="conf")
def module_function(cfg: DictConfig):
print(OmegaConf.to_yaml(cfg))
下面是Python主模块my_app.py
的内容:
from modules.module import module_function
def main():
module_function()
if __name__ == "__main__":
main()
当我尝试使用python my_app.py
运行主 Python 模块my_app.py
时,我立即收到一个错误说
Primary config module 'modules.conf' not found.
Check that it's correct and contains an __init__.py file
Set the environment variable HYDRA_FULL_ERROR=1 for a complete stack trace.
我知道这意味着添加到module_function
内部的装饰器module.py
找不到包含api_key和数据库配置组的conf
目录。
这里有人对此有任何经验并知道如何解决此错误吗?
首先,我将介绍一些可能的解决方案,然后我将给出一个解释。 您可以执行以下三件事来使示例正常工作:
修复1:创建conf/__init__.py
并将config_path="conf"
更改为config_path="../conf"
:
├── conf
│ ├── config.yaml
│ └── __init__.py
├── modules
│ └── module.py
└── my_app.py
# modules/module.py
import hydra
from omegaconf import DictConfig, OmegaConf
@hydra.main(config_path="../conf", config_name="config") # relative path
def module_function(cfg: DictConfig):
print(OmegaConf.to_yaml(cfg))
修复2:将conf
移动到modules/conf
并创建modules/conf/__init__.py
:
├── modules
│ ├── conf
│ │ ├── config.yaml
│ │ └── __init__.py
│ └── module.py
└── my_app.py
修复3:在模块中定义您的装饰函数,该函数将调用为__main__
:
├── conf
│ └── config.yaml
└── my_app.py
# my_app.py
import hydra
from omegaconf import DictConfig, OmegaConf
@hydra.main(config_path="conf", config_name="config")
def module_function(cfg: DictConfig):
print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
module_function()
修复3不会让OP:)非常满意
解释
在您的示例中,由于您已经导入了修饰的函数(而不是在__main__
模块中定义它),Hydra 将参数config_path="conf"
解释为相对于定义修饰函数的模块的父级。在你的例子中,装饰函数是在modules.module
中定义的,其父级是modules
,所以 Hydra 正在寻找一个名为modules.conf
的 python 包。如果找到,将搜索此包以查找 yaml 文件。
上面的 Fix 2 采用确保modules/conf/__init__.py
存在的方法,因此modules.conf
实际上是一个 python 包。 同时,Fix 1 采用使用相对路径config_path="../conf"
的方法。然后,九头蛇会寻找一个名为conf
的顶级包。由于conf/__init__.py
存在,因此将找到此顶级conf
包并搜索 yaml 文件。为了发现这个包,Hydra 使用了 python 导入机制。这意味着 Hydra 能够发现位于 python 包中的 yaml 文件,这些文件位于$PYTHONPATH
上的其他位置或通过包管理器(如pip
)安装。
在 Fix 3 的情况下,我们有module_function.__module__ == "__main__"
,这意味着 Hydra 不能依靠 python 的导入机制来发现定义module_function
的位置。作为后备,Hydra 试图找到一个目录conf
(而不是包conf
);这就是为什么 Fix 3 不需要__init__.py
文件的原因。下面是实现此行为的关键代码行。请注意,conf
仍被视为相对路径,相对于包含定义修饰函数的文件的目录(而不是定义它的模块的父级)。这是实现此相对路径计算的函数(适用于模块大小写和目录情况)。