【问题标题】:Using Typer and Hydra together一起使用 Typer 和 Hydra
【发布时间】:2022-01-22 09:44:14
【问题描述】:

我有一个简单的Typer 应用程序:

import typer

app = typer.Typer()

@app.command()
def say_hi():
    print("Hi")

@app.callback()
def main():
    pass

if __name__ == "__main__":
    app()

我想使用 Hydra 来管理应用程序的配置,但是我不确定如何在不失去从 CLI 覆盖配置的能力的情况下做到这一点。

我的第一次尝试是:

import hydra
import typer
from omegaconf import DictConfig, OmegaConf

app = typer.Typer()

@app.command()
def say_hi():
    print("Hi")

@app.callback()
@hydra.main(config_path="conf", config_name="config")
def main(cfg: DictConfig) -> None:
    print(OmegaConf.to_yaml(cfg))

if __name__ == "__main__":
    app()

但我收到一条错误消息:

RuntimeError: Type not yet supported: <class 'omegaconf.dictconfig.DictConfig'>

如果我删除 DictConfig 类型注释,我会收到 cfg 丢失的错误。

我在 Hydra 文档中看到了 Compose API,它允许在没有装饰器的情况下初始化配置:

@app.callback()
def main() -> None:
    with initialize(config_path="conf", job_name="test_app"):
        cfg = compose(config_name="config")
        print(OmegaConf.to_yaml(cfg))

但在这种情况下,我似乎无法从命令行覆盖配置,因为 Typer 应用程序无法识别这些值。

有什么建议可以解决吗?

【问题讨论】:

    标签: python command-line-interface fb-hydra hydra-python typer


    【解决方案1】:

    compose 函数接受可选的覆盖字符串列表:

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

    您需要从命令行获取覆盖字符串列表,然后将该列表传递给compose。 这是使用 Typer 的示例;类似的模式可能适用于例如argparse 或单击。 (免责声明:我不是 Typer 专家)

    from typing import List, Optional
    
    import typer
    from omegaconf import OmegaConf, DictConfig
    
    from hydra import compose, initialize
    
    app = typer.Typer()
    
    def my_compose(overrides: Optional[List[str]]) -> DictConfig:
        with initialize(config_path="conf", job_name="test_app"):
            return compose(config_name="config", overrides=overrides)
    
    @app.command()
    def say_hi(overrides: Optional[List[str]] = typer.Argument(None)):
        print("HI!")
        print(f"Got {overrides=}")
        cfg = my_compose(overrides)
        print("\nHydra config:")
        print(OmegaConf.to_yaml(cfg))
    
    @app.command()
    def say_bye(overrides: Optional[List[str]] = typer.Argument(None)):
        cfg = my_compose(overrides)
        ...
        print("BYE!")
    
    if __name__ == "__main__":
        app()
    
    $ python my_app.py say-hi +foo=bar +baz=qux
    HI!
    Got overrides=('+foo=bar', '+baz=qux')
    
    Hydra config:
    foo: bar
    baz: qux
    
    $ python my_app.py say-bye
    BYE!
    

    【讨论】:

    • 感谢您的详尽回答!您知道不使用 Compose API 的解决方法吗?文档中提到Please avoid using the Compose API in cases where @hydra.main() can be used. Doing so forfeits many of the benefits of Hydra (e.g., Tab completion, Multirun, Working directory management, Logging management and more)
    • 问题是Typer和hydra.main都受sys.argv的状态控制。 (同时 Hydra 的 compose 不与 sys.argv 交互)。听起来在您的用例中,您希望某些argv 由 Typer 处理,而其中一些由 Hydra 处理。对吗?
    • 请参阅this github comment 以获取在调用以@hydra.main 装饰的函数之前对sys.argv 进行预处理的脚本示例。
    • 再次感谢! (我希望我能再次投票)
    猜你喜欢
    • 2022-10-20
    • 2021-10-15
    • 2014-10-07
    • 1970-01-01
    • 2015-06-13
    • 2021-02-21
    • 1970-01-01
    • 2016-04-18
    • 1970-01-01
    相关资源
    最近更新 更多