hydra: [Bug] Hydra resolver does not work in compose_api
🐛 Bug
Description
hydra:key
substitution does not work in interactive Python session and Jupyter notebook.
hydra.key
does, but it is not recommended (according to https://github.com/facebookresearch/hydra/issues/1786#issuecomment-914734857).
Checklist
- I checked on the latest version of Hydra
- I created a minimal repro (See this for tips).
To reproduce
** Minimal Code/Config snippet to reproduce **
conf/config.yaml
defaults:
- env: dev
conf/env/prod.yaml
spark:
appName: ${hydra:job.name}
conf/env/dev.yaml
spark:
appName: ${hydra.job.name}
run.py
from hydra import initialize, compose
with initialize(config_path="conf"):
conf = compose(config_name="config.yaml", return_hydra_config=True)
print(conf.env.spark.appName)
# prints current file name, e.g. "run"
with initialize(config_path="conf"):
conf = compose(config_name="config.yaml", return_hydra_config=True, overrides=["env=prod"])
print(conf.env.spark.appName)
# raises exception
** Stack trace/error message **
$ python run.py
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/dictconfig.py", line 357, in __getattr__
self._format_and_raise(key=key, value=None, cause=e)
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/base.py", line 196, in _format_and_raise
type_override=type_override,
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/_utils.py", line 821, in format_and_raise
_raise(ex, cause)
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/_utils.py", line 719, in _raise
raise ex.with_traceback(sys.exc_info()[2]) # set end OC_CAUSE=1 for full backtrace
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/dictconfig.py", line 351, in __getattr__
return self._get_impl(key=key, default_value=_DEFAULT_MARKER_)
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/dictconfig.py", line 446, in _get_impl
key=key, value=node, default_value=default_value
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/basecontainer.py", line 69, in _resolve_with_default
throw_on_resolution_failure=True,
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/base.py", line 622, in _maybe_resolve_interpolation
memo=memo if memo is not None else set(),
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/base.py", line 483, in _resolve_interpolation_from_parse_tree
parse_tree=parse_tree, node=value, key=key, parent=parent, memo=memo
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/base.py", line 669, in resolve_parse_tree
).with_traceback(sys.exc_info()[2])
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/base.py", line 662, in resolve_parse_tree
return visitor.visit(parse_tree)
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/antlr4/tree/Tree.py", line 34, in visit
return tree.accept(self)
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/grammar/gen/OmegaConfGrammarParser.py", line 205, in accept
return visitor.visitConfigValue(self)
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/grammar_visitor.py", line 101, in visitConfigValue
return self.visit(ctx.getChild(0))
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/antlr4/tree/Tree.py", line 34, in visit
return tree.accept(self)
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/grammar/gen/OmegaConfGrammarParser.py", line 339, in accept
return visitor.visitText(self)
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/grammar_visitor.py", line 298, in visitText
return self.visitInterpolation(c)
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/grammar_visitor.py", line 125, in visitInterpolation
return self.visit(ctx.getChild(0))
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/antlr4/tree/Tree.py", line 34, in visit
return tree.accept(self)
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/grammar/gen/OmegaConfGrammarParser.py", line 1030, in accept
return visitor.visitInterpolationResolver(self)
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/grammar_visitor.py", line 182, in visitInterpolationResolver
args_str=tuple(args_str),
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/base.py", line 653, in resolver_interpolation_callback
inter_args_str=args_str,
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/base.py", line 596, in _evaluate_custom_resolver
inter_args_str,
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/omegaconf/omegaconf.py", line 435, in resolver_wrapper
ret = resolver(*args, **kwargs)
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/hydra/core/utils.py", line 215, in <lambda>
lambda path: OmegaConf.select(cast(DictConfig, HydraConfig.get()), path),
File "/home/maxim/Repo/ds-demo-project/venv/lib/python3.7/site-packages/hydra/core/hydra_config.py", line 31, in get
raise ValueError("HydraConfig was not set")
omegaconf.errors.InterpolationResolutionError: ValueError raised while resolving interpolation: HydraConfig was not set
full_key: env.spark.appName
object_type=dict
Expected Behavior
Substitutionshydra:
are not causing an exception inside a context created by compose
function with return_hydra_config=True
.
HydraConfig
instance should be initialized.
System information
- Hydra Version : both
1.1.0
from pypi and2a9f181
commit from main branch - Python version : both 3.7.8 and 3.10.2
- Virtual environment type and version : venv
- Operating system : Linux x86_64 5.16.5-1
Additional context
Same behavior has been observed several times: #716 https://github.com/facebookresearch/hydra/issues/1786#issuecomment-913110496
About this issue
- Original URL
- State: open
- Created 2 years ago
- Reactions: 6
- Comments: 19 (2 by maintainers)
Just a note that I also ran into this problem. FYI my context:
${hydra:runtime.choices.xyz}
to access which config group was used (and this is failing withHydraConfig was not set
)I would expect
HydraConfig
to be available within mywith initialize(...)
context (possibly with not all fields that are found when using@hydra.main(...)
, but at least if we have the config we can manipulate it for the purpose of the test)Edit: my current workaround is the following (sketch):
Yes indeed, the ConfigStore is persistent global state.
For Hydra’s test suite, we have a
hydra_restore_singletons
fixture that is used to save and then later restore the global state so that the tests can be run independently.No, I was talking about
hydra.main
. Please disregard my previous comment.The root of the problem is that the
hydra:
resolver relies on global state (i.e. the state of theHydraConfig
singleton). When using thecompose
API, thehydra:
resolver fails because the global state (HydraConfig
) is not set up.It would be nice to have an API that has the best of both worlds from
@hydra.main
andcompose
:@hydra.main
allows for use of the thehydra:
resolver and access toHydraConfig
(within the context of the function decorated by@hydra.main
). However,hydra.main
does not easily allow control over the overrides via python code.compose
allows passingoverrides
and its use is consistent with the functional programming paradigm, but access toHydraConfig
is not available.Again brainstorming about possible APIs, it would be nice if
compose
could function as a context manager:Thanks for the link to #716. The context is useful. For now I will put this issue on the wishlist.
Any progress here?
Any update on this?
@Jasha10, Utilizing a context is a nice idea, but I think you should not abuse compose(). How about adding a new context for it?
Alternatively, you can just document in the Compose API page how to regain access to the Hydra config (like the example from @odelalleau).
Any updates on this?
Thanks for looking into this @odelalleau! I’ll try to put together an implementation for
compose_context
.I’m thinking that a usage pattern involving
OmegaConf.resolve
could even make thecfg
object behave nicely after the context has ended:Thanks for the report, @dolfinus. I can reproduce the behavior from the example you provided.
The issue here is that the
hydra:
resolver (${hydra:...}
) depends on global state, namely the state of HydraConfig. When using@hydra.main
, using thehydra:
resolver results in a query to this global state.Meanwhile, the compose API is a functional API; after the call to
compose
has returned, the global HydraConfig state is not retained. This is why we are getting the “HydraConfig was not set” ValueError in the traceback above.I’m not sure what the best way to get around this is… Is your usecase limited to looking up
hydra.job.name
? Or is there some other hydra setting that you wanted to query?