Repository: facebookresearch/hydra

Issue:
[Feature Request] Add a resolve flag to when using --cfg to output the config
# 🚀 Feature Request

Add some way to print the resolved configuration (for interpolations) when using the `--cfg` flag in the CLI. 

## Motivation

**Is your feature request related to a problem? Please describe.**

OmegaConf evaluates interpolated values lazily, which is a bummer when using the `--cfg` flag to output the config for debug purposes, since the values just print un-interpolated.

## Pitch

**Describe the solution you'd like**
An extra flag, maybe `--resolve` in the CLI to print the resolved configuration.

**Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->

An alternative is to just use `print(OmegaConf.to_yaml(cfg, resolve=True))` in the code, but it would be better to not clutter the code with such a statement.

**Are you willing to open a pull request?** (See [CONTRIBUTING](../../CONTRIBUTING.md))

Yes, but I'd need a pointer for where to look in the code.



Discussion:
Thanks @davidcian  for the request! We will look into adding this for Hydra 1.1

FYI I am working on this currently.
thanks @Jasha10, I tried to assign the issue to you on GitHub but it won't let me 🤔 , then I ended up clicking the wrong button and accidentally assigned it to myself 😂

Patch:
diff --git a/hydra/_internal/hydra.py b/hydra/_internal/hydra.py
--- a/hydra/_internal/hydra.py
+++ b/hydra/_internal/hydra.py
@@ -179,6 +179,7 @@ def show_cfg(
         overrides: List[str],
         cfg_type: str,
         package: Optional[str],
+        resolve: bool = False,
     ) -> None:
         cfg = self._get_cfg(
             config_name=config_name,
@@ -188,19 +189,23 @@ def show_cfg(
         )
         if package == "_global_":
             package = None
-        if package is not None:
+
+        if package is None:
+            ret = cfg
+        else:
             ret = OmegaConf.select(cfg, package)
             if ret is None:
                 sys.stderr.write(f"package '{package}' not found in config\n")
                 sys.exit(1)
-            else:
-                if isinstance(ret, Container):
-                    print(f"# @package {package}")
-                    sys.stdout.write(OmegaConf.to_yaml(ret))
-                else:
-                    print(ret)
+
+        if not isinstance(ret, Container):
+            print(ret)
         else:
-            sys.stdout.write(OmegaConf.to_yaml(cfg))
+            if package is not None:
+                print(f"# @package {package}")
+            if resolve:
+                OmegaConf.resolve(ret)
+            sys.stdout.write(OmegaConf.to_yaml(ret))
 
     @staticmethod
     def get_shell_to_plugin_map(
diff --git a/hydra/_internal/utils.py b/hydra/_internal/utils.py
--- a/hydra/_internal/utils.py
+++ b/hydra/_internal/utils.py
@@ -355,10 +355,14 @@ def add_conf_dir() -> None:
         )
         if num_commands > 1:
             raise ValueError(
-                "Only one of --run, --multirun,  -cfg, --info and --shell_completion can be specified"
+                "Only one of --run, --multirun, --cfg, --info and --shell_completion can be specified"
             )
         if num_commands == 0:
             args.run = True
+        if args.resolve and not has_show_cfg:
+            raise ValueError(
+                "The --resolve flag can only be used in conjunction with --cfg"
+            )
         if args.run:
             run_and_report(
                 lambda: hydra.run(
@@ -382,6 +386,7 @@ def add_conf_dir() -> None:
                     overrides=args.overrides,
                     cfg_type=args.cfg,
                     package=args.package,
+                    resolve=args.resolve,
                 )
             )
         elif args.shell_completion:
@@ -452,6 +457,11 @@ def get_args_parser() -> argparse.ArgumentParser:
         choices=["job", "hydra", "all"],
         help="Show config instead of running [job|hydra|all]",
     )
+    parser.add_argument(
+        "--resolve",
+        action="store_true",
+        help="Used in conjunction with --cfg, resolve config interpolations before printing.",
+    )
 
     parser.add_argument("--package", "-p", help="Config package to show")
 


Test patch:
diff --git a/tests/test_apps/simple_interpolation/config.yaml b/tests/test_apps/simple_interpolation/config.yaml
new file mode 100644
--- /dev/null
+++ b/tests/test_apps/simple_interpolation/config.yaml
@@ -0,0 +1,2 @@
+a: ${b}
+b: 10
diff --git a/tests/test_apps/simple_interpolation/my_app.py b/tests/test_apps/simple_interpolation/my_app.py
new file mode 100644
--- /dev/null
+++ b/tests/test_apps/simple_interpolation/my_app.py
@@ -0,0 +1,13 @@
+# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
+from omegaconf import DictConfig
+
+import hydra
+
+
+@hydra.main(config_path=".", config_name="config")
+def my_app(cfg: DictConfig) -> None:
+    print(cfg.c)
+
+
+if __name__ == "__main__":
+    my_app()
diff --git a/tests/test_hydra.py b/tests/test_hydra.py
--- a/tests/test_hydra.py
+++ b/tests/test_hydra.py
@@ -382,22 +382,26 @@ def test_module_env_override(tmpdir: Path, env_name: str) -> None:
     assert OmegaConf.create(result) == {"normal_yaml_config": True}
 
 
+@mark.parametrize("resolve", [True, False])
 @mark.parametrize(
     "flag,expected_keys",
     [("--cfg=all", ["db", "hydra"]), ("--cfg=hydra", ["hydra"]), ("--cfg=job", ["db"])],
 )
-def test_cfg(tmpdir: Path, flag: str, expected_keys: List[str]) -> None:
+def test_cfg(tmpdir: Path, flag: str, resolve: bool, expected_keys: List[str]) -> None:
     cmd = [
         "examples/tutorials/basic/your_first_hydra_app/5_defaults/my_app.py",
         "hydra.run.dir=" + str(tmpdir),
         flag,
     ]
+    if resolve:
+        cmd.append("--resolve")
     result, _err = run_python_script(cmd)
     conf = OmegaConf.create(result)
     for key in expected_keys:
         assert key in conf
 
 
+@mark.parametrize("resolve", [True, False])
 @mark.parametrize(
     "flags,expected",
     [
@@ -440,16 +444,71 @@ def test_cfg(tmpdir: Path, flag: str, expected_keys: List[str]) -> None:
         param(["--cfg=job", "--package=db.driver"], "mysql\n", id="package=db.driver"),
     ],
 )
-def test_cfg_with_package(tmpdir: Path, flags: List[str], expected: str) -> None:
+def test_cfg_with_package(
+    tmpdir: Path, flags: List[str], resolve: bool, expected: str
+) -> None:
     cmd = [
         "examples/tutorials/basic/your_first_hydra_app/5_defaults/my_app.py",
         "hydra.run.dir=" + str(tmpdir),
     ] + flags
+    if resolve:
+        cmd.append("--resolve")
 
     result, _err = run_python_script(cmd)
     assert normalize_newlines(result) == expected.rstrip()
 
 
+@mark.parametrize(
+    "resolve,expected",
+    [
+        param(
+            False,
+            dedent(
+                """\
+                a: ${b}
+                b: 10
+                """
+            ),
+            id="cfg",
+        ),
+        param(
+            True,
+            dedent(
+                """\
+                a: 10
+                b: 10
+                """
+            ),
+            id="resolve",
+        ),
+    ],
+)
+def test_cfg_resolve_interpolation(tmpdir: Path, resolve: bool, expected: str) -> None:
+    cmd = [
+        "tests/test_apps/simple_interpolation/my_app.py",
+        "hydra.run.dir=" + str(tmpdir),
+        "--cfg=job",
+    ]
+    if resolve:
+        cmd.append("--resolve")
+
+    result, _err = run_python_script(cmd)
+    assert_text_same(result, expected)
+
+
+def test_resolve_flag_without_cfg_flag(tmpdir: Path) -> None:
+    cmd = [
+        "examples/tutorials/basic/your_first_hydra_app/3_using_config/my_app.py",
+        "hydra.run.dir=" + str(tmpdir),
+        "--resolve",
+    ]
+
+    err = run_with_error(cmd)
+    assert normalize_newlines(err).endswith(
+        "ValueError: The --resolve flag can only be used in conjunction with --cfg"
+    )
+
+
 @mark.parametrize(
     "calling_file, calling_module",
     [
@@ -548,6 +607,7 @@ def test_sweep_complex_defaults(
                 --hydra-help : Hydra's help
                 --version : Show Hydra's version and exit
                 --cfg,-c : Show config instead of running [job|hydra|all]
+                --resolve : Used in conjunction with --cfg, resolve config interpolations before printing.
                 --package,-p : Config package to show
                 --run,-r : Run a job
                 --multirun,-m : Run multiple jobs with the configured launcher and sweeper
@@ -604,6 +664,7 @@ def test_sweep_complex_defaults(
                 --hydra-help : Hydra's help
                 --version : Show Hydra's version and exit
                 --cfg,-c : Show config instead of running [job|hydra|all]
+                --resolve : Used in conjunction with --cfg, resolve config interpolations before printing.
                 --package,-p : Config package to show
                 --run,-r : Run a job
                 --multirun,-m : Run multiple jobs with the configured launcher and sweeper
@@ -1278,6 +1339,8 @@ def test_hydra_main_without_config_path(tmpdir: Path) -> None:
     [
         (["--cfg", "job", "-p", "baud_rate"], "19200"),
         (["--cfg", "hydra", "-p", "hydra.job.name"], "frozen"),
+        (["--cfg", "job", "--resolve", "-p", "baud_rate"], "19200"),
+        (["--cfg", "hydra", "--resolve", "-p", "hydra.job.name"], "frozen"),
         (["--info", "config"], "baud_rate: 19200"),
         (["--hydra-help"], "== Flags =="),
         (["--help"], "frozen is powered by Hydra."),