How to reload hydra config with enumerations
Is there a better way to reload a hydra config from an experiment with enumerations? Right now I reload it like so:
initialize_config_dir(config_dir=exp_dir, &quot;.hydra&quot;), job_name=config_name)
cfg = compose(config_name, overrides=overrides)
print(cfg.enum)
&gt;&gt;&gt; ENUM1

But ENUM1 is actually an enumeration that normally loads as
&gt;&gt;&gt; &lt;SomeEnumClass.ENUM1: 'enum1'&gt;

I am able to fix this by adding a configstore default to the experiment hydra file:
defaults:
  - base_config_cs

Which now results in
initialize_config_dir(config_dir=exp_dir, &quot;.hydra&quot;), job_name=config_name)
cfg = compose(config_name, overrides=overrides)
print(cfg.enum)
&gt;&gt;&gt; &lt;SomeEnumClass.ENUM1: 'enum1'&gt;

Is there a better way to do this without adding this? Or can I add the default in the python code?

This is a good question -- reliably reloading configs from previous Hydra runs is an area that could be improved.
As you've discovered, loading the saved file config.yaml directly results in an untyped DictConfig object.
The solution below involves a script called reload.py that creates a config node with a defaults list that loads both the schema base_config_cs and the saved file config.yaml.
At the end of this post I also give a simple solution that involves loading .hydra/overrides.yaml to re-run the config composition process.

Suppose you've run a Hydra job with the following setup:
# app.py
from dataclasses import dataclass
from enum import Enum
import hydra
from hydra.core.config_store import ConfigStore
from omegaconf import DictConfig

class SomeEnumClass(Enum):
    ENUM1 = 1
    ENUM2 = 2

@dataclass
class Schema:
    enum: SomeEnumClass
    x: int = 123
    y: str = &quot;abc&quot;

def store_schema() -&gt; None:
    cs = ConfigStore.instance()
    cs.store(name=&quot;base_config_cs&quot;, node=Schema)

@hydra.main(config_path=&quot;.&quot;, config_name=&quot;foo&quot;)
def app(cfg: DictConfig) -&gt; None:
    print(cfg)

if __name__ == &quot;__main__&quot;:
    store_schema()
    app()

# foo.yaml
defaults:
  - base_config_cs
  - _self_
enum: ENUM1
x: 456

$ python app.py y=xyz
{'enum': &lt;SomeEnumClass.ENUM1: 1&gt;, 'x': 456, 'y': 'xyz'}

After running app.py, there exists a directory outputs/2022-02-05/06-42-42/.hydra containing the saved file config.yaml.
As you correctly pointed out in your question, to reload the saved config you must merge the schema base_config_cs with the contents of config.yaml. Here is a pattern for accomplishing that:
# reload.py
import os
from hydra import compose, initialize_config_dir
from hydra.core.config_store import ConfigStore
from app import store_schema

config_name = &quot;config&quot;
exp_dir = os.path.abspath(&quot;outputs/2022-02-05/07-19-56&quot;)
saved_cfg_dir = os.path.join(exp_dir, &quot;.hydra&quot;)
assert os.path.exists(f&quot;{saved_cfg_dir}/{config_name}.yaml&quot;)

store_schema()  # stores `base_config_cs`
cs = ConfigStore.instance()
cs.store(
    name=&quot;reload_conf&quot;,
    node={
        &quot;defaults&quot;: [
            &quot;base_config_cs&quot;,
            config_name,
        ]
    },
)

with initialize_config_dir(config_dir=saved_cfg_dir):
    cfg = compose(&quot;reload_conf&quot;)
print(cfg)

$ python reload.py
{'enum': &lt;SomeEnumClass.ENUM1: 1&gt;, 'x': 456, 'y': 'xyz'}

In the above, python file reload.py, we store a node called reload_conf in the ConfigStore. Storing reload_conf this way is equivalent to creating a file called reload_conf.yaml that is discoverable by Hydra on the config search path. This reload_conf node has a defaults list that loads both the schema base_config_cs and config. For this to work, the following two conditions must be met:

the schema base_config_cs must be stored in the ConfigStore. This is accomplished by calling the store_schema function that we have imported from app.py.
a config node with name specified by the variable config_name, i.e. config.yaml in this example, must be discoverable by Hydra (which is taken care of here by calling initialize_config_dir).

Note that in foo.yaml we have a defaults list [&quot;base_config_cs&quot;, &quot;_self_&quot;] that loads the schema base_config_cs before loading the contents _self_ of foo. In order for reload_conf to reconstruct the app's config with the same merge order, base_config_cs should come before config_name in the defaults list belonging to reload_conf.

The above approach could be taken one step further by removing the defaults list from foo.yaml and using cs.store to ensure the same defaults list is used in both the app and the reloading script
# app2.py
from dataclasses import dataclass
from enum import Enum
from typing import Any, List
import hydra
from hydra.core.config_store import ConfigStore
from omegaconf import MISSING, DictConfig

class SomeEnumClass(Enum):
    ENUM1 = 1
    ENUM2 = 2

@dataclass
class RootConfig:
    defaults: List[Any] = MISSING
    enum: SomeEnumClass = MISSING
    x: int = 123
    y: str = &quot;abc&quot;

def store_root_config(primary_config_name: str) -&gt; None:
    cs = ConfigStore.instance()
    # defaults list defined here:
    cs.store(
        name=&quot;root_config&quot;, node=RootConfig(defaults=[&quot;_self_&quot;, primary_config_name])
    )

@hydra.main(config_path=&quot;.&quot;, config_name=&quot;root_config&quot;)
def app(cfg: DictConfig) -&gt; None:
    print(cfg)

if __name__ == &quot;__main__&quot;:
    store_root_config(&quot;foo2&quot;)
    app()

# foo2.yaml (note NO DEFAULTS LIST)
enum: ENUM1
x: 456

$ python app2.py hydra.job.chdir=false y=xyz
{'enum': &lt;SomeEnumClass.ENUM1: 1&gt;, 'x': 456, 'y': 'xyz'}

# reload2.py
import os
from hydra import compose, initialize_config_dir
from hydra.core.config_store import ConfigStore
from app2 import store_root_config

config_name = &quot;config&quot;
exp_dir = os.path.abspath(&quot;outputs/2022-02-05/07-45-43&quot;)
saved_cfg_dir = os.path.join(exp_dir, &quot;.hydra&quot;)
assert os.path.exists(f&quot;{saved_cfg_dir}/{config_name}.yaml&quot;)

store_root_config(&quot;config&quot;)
with initialize_config_dir(config_dir=saved_cfg_dir):
    cfg = compose(&quot;root_config&quot;)
print(cfg)

$ python reload2.py
{'enum': &lt;SomeEnumClass.ENUM1: 1&gt;, 'x': 456, 'y': 'xyz'}


A simpler alternative approach is to use .hydra/overrides.yaml to recompose the app's configuration based on the overrides that were originally passed to Hydra:
# reload3.py
import os
import yaml
from hydra import compose, initialize
from app import store_schema

config_name = &quot;config&quot;
exp_dir = os.path.abspath(&quot;outputs/2022-02-05/07-19-56&quot;)
saved_cfg_dir = os.path.join(exp_dir, &quot;.hydra&quot;)
overrides_path = f&quot;{saved_cfg_dir}/overrides.yaml&quot;
assert os.path.exists(overrides_path)

overrides = yaml.unsafe_load(open(overrides_path, &quot;r&quot;))
print(f&quot;{overrides=}&quot;)
store_schema()
with initialize(config_path=&quot;.&quot;):
    cfg = compose(&quot;foo&quot;, overrides=overrides)
print(cfg)

$ python reload3.py
overrides=['y=xyz']
{'enum': &lt;SomeEnumClass.ENUM1: 1&gt;, 'x': 456, 'y': 'xyz'}

This approach has its drawbacks: if your app's configuration involves some non-hermetic operation like querying a timestamp (e.g. via Hydra's now resolver) or looking up an environment variable (e.g. via the oc.env resolver), the configuration composed by reload.py might be different from the original version loaded in app.py.
