omegaconf
omegaconf copied to clipboard
Feature: nested string interpolations
Currently nested interpolations, e.g. "${b_${c}}"
, do not seem to be supported.
Here is an example:
from omegaconf import OmegaConf
cfg = OmegaConf.create(
{
"a": "${b_${c}}",
"c": 123,
"b_123": "xyz",
}
)
print(cfg.a)
The desired output would be xyz
.
The current behavior is to throw a GrammarParserError
.
Describe alternatives you've considered
Here is a workaround that uses getattr
to explicitly dereference the interpolation:
cfg = OmegaConf.create(
{
"a_ptr": "b_${c}",
"c": 123,
"b_123": "xyz",
}
)
print(getattr(cfg, cfg.a_ptr))
Additional context
>>> omegaconf.__version__
'2.1.0dev20'
String interpolations does not support nested interpolations right now. You can see examples of supported forms of nested interpolations in the docs. I am not sure how hard it would be to add (@odelalleau, I am pretty sure you thought about it, what do you think?). We should probably either enable it or document that it's not supported at this time.
@Jasha10, do you have an actual use case or are you just pointing it out because it surprised you?
I am not sure how hard it would be to add (@odelalleau, I am pretty sure you thought about it, what do you think?).
IIRC at some point you mentioned that there was no need to support it so it's not something I kept -- pretty sure it was possible in one of my many versions :) (but I may not be remembering correctly, it's been a while -- probably sometime around July/August 2020)
There are some workarounds in case someone really needs it:
# Use an intermediate variable to hold the full name
cfg = OmegaConf.create(
{
"a_ref": "b_${c}",
"a": "${${a_ref}}",
"c": 123,
"b_123": "xyz",
}
)
print(cfg.a)
# Use a resolver
OmegaConf.register_new_resolver("identity", lambda x: x)
cfg = OmegaConf.create(
{
"a": "${${identity:b_${c}}}",
"c": 123,
"b_123": "xyz",
}
)
print(cfg.a)
In terms of enabling it in the grammar, it wouldn't be too hard, it just wouldn't play nice with #600 => would need to change a bit the approach taken here (handling $
in the parser instead of the lexer -- it's doable, actually my first version was doing that before I realized it was simpler to do it at the lexer level).
Yes, I do have a use case. I'll try to come up with a minimal practical example...
Edit: for future reference, here is my motivating example:
@dataclass
class Config:
provider: str # provider1 or provider2
service: str # serviceA or serviceB
message_format: str = "${message_format_table.${provider}_${service}}"
message_format_table: Dict[str, str] = ...
Yeah yeah, blame me. I was probably just trying to cut down on the scope :P.
If anyone wants to try to enable it go ahead but I am learning toward revisiting in 2.2.
There are some workarounds in case someone really needs it:
# Use an intermediate variable to hold the full name cfg = OmegaConf.create( { "a_ref": "b_${c}", "a": "${${a_ref}}", ...
I wasn't aware that this was possible. Thanks for the tip!
If anyone wants to try to enable it go ahead but I am learning toward revisiting in 2.2.
Not going for it myself but @Jasha10 if you want to take a stab at it let me know, I'll give you some pointers.
Ok, sounds good :)
Edit: just reread the above, I think revisiting in 2.2 sounds better given that there exist viable workarounds.