omry / omegaconf

Flexible Python configuration system. The last one you will ever need.
BSD 3-Clause "New" or "Revised" License
1.91k stars 104 forks source link

`unsafe_merge` crashes with nested structured config and union type #1087

Open function2-llx opened 1 year ago

function2-llx commented 1 year ago

Describe the bug unsafe_merge crashes with nested structured config and union type.

To Reproduce

from dataclasses import dataclass

from omegaconf import OmegaConf

@dataclass
class A:
    x: int | str

@dataclass
class B:
    a: A

def main():
    x = OmegaConf.unsafe_merge(OmegaConf.structured(B), {'a': {'x': 1}})
    print(x)

if __name__ == '__main__':
    main()

Expected behavior {'a': {'x': 1}} will be printed, just like OmegaConf.merge.

Actual result

Traceback (most recent call last):
  File "./test.py", line 18, in <module>
    main()
  File "./test.py", line 14, in main
    x = OmegaConf.unsafe_merge(OmegaConf.structured(B), {'a': {'x': 1}})
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../omegaconf/omegaconf.py", line 308, in unsafe_merge
    target.merge_with(*configs[1:])
  File ".../omegaconf/basecontainer.py", line 492, in merge_with
    self._format_and_raise(key=None, value=None, cause=e)
  File ".../omegaconf/base.py", line 231, in _format_and_raise
    format_and_raise(
  File ".../omegaconf/basecontainer.py", line 490, in merge_with
    self._merge_with(*others)
  File ".../omegaconf/basecontainer.py", line 514, in _merge_with
    BaseContainer._map_merge(self, other)
  File ".../omegaconf/basecontainer.py", line 399, in _map_merge
    dest_node._merge_with(src_node)
  File ".../omegaconf/basecontainer.py", line 514, in _merge_with
    BaseContainer._map_merge(self, other)
  File ".../omegaconf/basecontainer.py", line 358, in _map_merge
    expand(dest)
  File ".../omegaconf/basecontainer.py", line 342, in expand
    node._set_value(val)
  File ".../omegaconf/dictconfig.py", line 647, in _set_value
    raise e
  File ".../omegaconf/dictconfig.py", line 644, in _set_value
    self._set_value_impl(value, flags)
  File ".../omegaconf/dictconfig.py", line 677, in _set_value_impl
    self.__setitem__(k, v)
  File ".../omegaconf/dictconfig.py", line 314, in __setitem__
    self._format_and_raise(key=key, value=value, cause=e)
  File ".../omegaconf/base.py", line 231, in _format_and_raise
    format_and_raise(
  File ".../omegaconf/dictconfig.py", line 308, in __setitem__
    self.__set_impl(key=key, value=value)
  File ".../omegaconf/dictconfig.py", line 318, in __set_impl
    self._set_item_impl(key, value)
  File ".../omegaconf/basecontainer.py", line 535, in _set_item_impl
    if self._get_root() is value._get_root():
                           ^^^^^^^^^^^^^^^^^
  File ".../omegaconf/base.py", line 289, in _get_root
    assert isinstance(self, Container)
AssertionError

Additional context

Jasha10 commented 1 year ago

Thanks, @function2-llx. I can reproduce the error.