如何在不使用 @hydra.main() 的情况下获取 Hydra 配置

How to get a Hydra config without using @hydra.main()

假设我们有以下设置(从 Hydra docs 复制和缩短):

配置文件:config.yaml

db:
  driver: mysql
  user: omry
  pass: secret

Python 文件:my_app.py

import hydra
@hydra.main(config_path="config.yaml")
def my_app(cfg):
    print(cfg.pretty())
if __name__ == "__main__":
    my_app()

当我们可以在函数 my_app 上使用装饰器时,这很有效。现在我想(为了小脚本和测试目的,但这并不重要)在任何函数之外获取这个 cfg 对象,只是在一个普通的 python 脚本中。据我了解装饰器的工作原理,应该可以调用

import hydra
cfg = hydra.main(config_path="config.yaml")(lambda x: x)()
print(cfg.pretty())

但是 cfg 只是 None 而不是所需的配置对象。所以看起来装饰器没有传递返回值。还有其他方法可以达到 cfg 吗?

我找到了一个相当丑陋的答案,但它有效 - 如果有人找到更优雅的解决方案,请告诉我们!

我们可以使用闭包或一些可变对象。在这个例子中,我们在外部定义了一个列表并附加了配置对象:

对于 hydra >= 1.0.0,您必须改用 config_name,请参阅 documentation

import hydra
c = []
hydra.main(config_name="config.yaml")(lambda x:c.append(x))()
cfg = c[0]
print(cfg)

旧版本:

import hydra
c = []
hydra.main(config_path="config.yaml")(c.append)()
cfg = c[0]
print(cfg.pretty())

使用 Compose API:

from hydra import compose, initialize
from omegaconf import OmegaConf

initialize(config_path="conf", job_name="test_app")
cfg = compose(config_name="config", overrides=["db=mysql", "db.user=me"])
print(OmegaConf.to_yaml(cfg))

这只会构成配置,不会产生副作用,例如更改工作目录或配置 Python 日志系统。

另一个丑陋的答案,但作者说这可能会在下一个版本中被粉碎

Blockquote

from omegaconf import DictConfig
from hydra.utils import instantiate
from hydra._internal.utils import _strict_mode_strategy, split_config_path, create_automatic_config_search_path
from hydra._internal.hydra import Hydra
from hydra.utils import get_class 

class SomeThing:
...
def load_from_yaml(self, config_path, strict=True):
    config_dir, config_file = split_config_path(config_path)
    strict = _strict_mode_strategy(strict, config_file)
    search_path = create_automatic_config_search_path(
        config_file, None, config_dir
    )
    hydra = Hydra.create_main_hydra2(
        task_name='sdfs', config_search_path=search_path, strict=strict
    )
    config = hydra.compose_config(config_file, [])
    config.pop('hydra')
    self.config = config
    print(self.config.pretty())

None 以上解决方案对我有用。他们给出了错误:

'builtin_function_or_method' object has no attribute 'code'

GlobalHydra is already initialized, call Globalhydra.instance().clear() if you want to re-initialize

我深入研究了 hydra 并意识到我可以只使用 OmegaConf 直接加载文件。你不会得到覆盖,但我对此并不担心。

import omegaconf
cfg = omegaconf.OmegaConf.load(path)

这是我的解决方案

from omegaconf import OmegaConf

class MakeObj(object):
    """ dictionary to object. 
    Thanks to 

    Args:
        object ([type]): [description]
    """
    def __init__(self, d):
        for a, b in d.items():
            if isinstance(b, (list, tuple)):
                setattr(self, a, [MakeObj(x) if isinstance(x, dict) else x for x in b])
            else:
                setattr(self, a, MakeObj(b) if isinstance(b, dict) else b)


def read_yaml(path):
    x_dict =  OmegaConf.load(path)
    x_yamlstr = OmegaConf.to_yaml(x_dict)
    x_obj = MakeObj(x_dict)
    return x_yamlstr, x_dict, x_obj
    
x_yamlstr, x_dict, x_obj = read_yaml('config/train.yaml')
print(x_yamlstr)
print(x_dict)
print(x_obj)
print(dir(x_obj))