Repository: omry/omegaconf

Issue #472: Convert Structured Config back to dataclass instance
Created: 2021-01-16T00:12:09Z
Is your feature request related to a problem? Please describe.
OmegaConf.structured() returns a DictConfig, but it is recommended to duck type that as the original structured config type to get a static type checker to catch errors. But this seems like a leaky abstraction:
import attr
from omegaconf import OmegaConf
from typing import List

@attr.s(auto_attribs=True)
class Widget:
   foo: str
   bar: int

@attr.s(auto_attribs=True)
class Quux:
   widget_list: List[Widget]

In [19]:
qq: Quux = OmegaConf.structured(Quux(widget_list=[Widget(foo='a', bar=1)]))

In [27]:
type(qq.widget_list[0])
Out [27]:
omegaconf.dictconfig.DictConfig

def do_something(conf: Quux):
   print(conf.widget_list[0].foo)
For example, in the above do_something function, we are annotating conf: Quux, and this is ok as long as we don't do anything expecting conf.widget_list[0] to be an actual Widget instance. If I wanted to convert a conf.widget_list to JSON, I would do something different for a "real" Quux instance and for a duck-typed instance.
My naive understanding is that this is so you can add runtime validation for merging etc.
Describe the solution you'd like
I think it would be nice to have a way to re-instantiate the original type after merging etc:
qq = OmegaConf.instantiate(
   OmegaConf.merge(OmegaConf.structured(Quux), OmegaConf.load(...))
)
assert type(qq) is Quux
The benefit I'm imagining is that code that is annotated with Quux would not have any "leaks".  Code that is concerned with manipulating configs can continue to use DictConfigs and their runtime features, but code that is using configs can work with the original types.
What stops us from doing so?
Describe alternatives you've considered


Another nice-to-have would be for OmegaConf.structured to return a DictConfig[Quux] -- ie. a generic so that this can be annotated clearly.


I think that if I were implementing this library from scratch, I would start with attrs/structured configs only, and then add the runtime validation via attrs's hooks like on_setattr etc. - we would never have to enter DictConfig-land. What are the problems with this approach (setting side the fact that dictconfigs are widely used already)?


Additional context
Add any other context or screenshots about the feature request here.

Duplicate issue #480: Setting non-dictconfig attributes
Created: 2021-01-19T13:40:19Z
Hi @omry! First of all, thanks for the work you've put into this library!
I was wondering if there is any way to set a DictConfig attribute to a dataclass, without it getting converted into a DictConfig
from dataclasses import dataclass
from omegaconf import OmegaConf

@dataclass
class Foo:
    bar: int = 1

# baseline
x = OmegaConf.create({"foo": {"bar": 1}})
print(type(x["foo"]), x)

# assignment
x["foo"] = Foo()
print(type(x["foo"]), x)

# update
OmegaConf.update(x, "foo", Foo())
print(type(x["foo"]), x)

# creation
x = OmegaConf.create({"foo": Foo()})
print(type(x["foo"]), x)
All of these print the same output and convert Foo() into {bar: 1}
My use case is wanting to parse some of the DictConfig attributes into dataclasses, while keeping others as DictConfigs.
Thank you!

Closing comment (2021-01-19T22:20:24Z): Hi @carmocca!
No, this is a fundamental design choice.
The dataclass objects are assumed to be Structured Configs and are converted to a type safe DictConfig.
You can access the underlying type with OmegaConf.get_type(cfg), and in principle you can instantiate the dataclass again from the data in the config.
This is not implemented now and has a large surface area. I am open to a pull request to add this functionality.
See my comment on #472.
Closing as a dup of #472 (but feel free to continue the discussion here).