Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: set flag readonly recursively #1124

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion docs/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -539,9 +539,10 @@ Configuration flags
-------------------

OmegaConf support several configuration flags.
Configuration flags can be set on any configuration node (Sequence or Mapping). if a configuration flag is not set
Configuration flags can be set on any configuration node (Sequence or Mapping). If a configuration flag is not set
it inherits the value from the parent of the node.
The default value inherited from the root node is always false.
To avoid this inhertence and set a flag explicitly to all children nodes, specify the option `recursive=True`.

.. _read-only-flag:

Expand Down Expand Up @@ -570,6 +571,17 @@ You can temporarily remove the read only flag from a config object:
>>> conf.a.b
20

Example using `recursive=True`:

.. doctest:: loaded

>>> conf = OmegaConf.create({"a": {"b": 10}})
>>> OmegaConf.set_readonly(conf.a, True)
>>> OmegaConf.set_readonly(conf, False, recursive = True)
>>> conf.a.b = 20
>>> conf.a.b
20

.. _struct-flag:

Struct flag
Expand Down
1 change: 1 addition & 0 deletions news/1124.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OmegaConf.set_readonly() and OmegaConf.set_struct() have an option recursive, that explicitly sets the flag to all children nodes
16 changes: 16 additions & 0 deletions omegaconf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def _set_flag(
self,
flags: Union[List[str], str],
values: Union[List[Optional[bool]], Optional[bool]],
recursive: bool = False,
) -> "Node":
if isinstance(flags, str):
flags = [flags]
Expand All @@ -175,6 +176,21 @@ def _set_flag(
assert self._metadata.flags is not None
self._metadata.flags[flag] = value
self._invalidate_flags_cache()

if recursive:
from . import DictConfig, ListConfig

if isinstance(self, DictConfig):
for key in self.keys():
child = self._get_child(key)
if child is not None:
child._set_flag(flags, values, recursive=recursive) # type: ignore
elif isinstance(self, ListConfig):
for index in range(len(self)):
child = self._get_child(index)
if child is not None:
child._set_flag(flags, values, recursive=recursive) # type: ignore

return self

def _get_node_flag(self, flag: str) -> Optional[bool]:
Expand Down
12 changes: 8 additions & 4 deletions omegaconf/omegaconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,19 +518,23 @@ def copy_cache(from_config: BaseContainer, to_config: BaseContainer) -> None:
OmegaConf.set_cache(to_config, OmegaConf.get_cache(from_config))

@staticmethod
def set_readonly(conf: Node, value: Optional[bool]) -> None:
def set_readonly(
conf: Node, value: Optional[bool], recursive: bool = False
) -> None:
# noinspection PyProtectedMember
conf._set_flag("readonly", value)
conf._set_flag("readonly", value, recursive=recursive)

@staticmethod
def is_readonly(conf: Node) -> Optional[bool]:
# noinspection PyProtectedMember
return conf._get_flag("readonly")

@staticmethod
def set_struct(conf: Container, value: Optional[bool]) -> None:
def set_struct(
conf: Container, value: Optional[bool], recursive: bool = False
) -> None:
# noinspection PyProtectedMember
conf._set_flag("struct", value)
conf._set_flag("struct", value, recursive=recursive)

@staticmethod
def is_struct(conf: Container) -> Optional[bool]:
Expand Down
19 changes: 19 additions & 0 deletions tests/test_base_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,25 @@ def test_set_flags() -> None:
c._set_flag(["readonly", "struct"], [True, False, False])


@mark.parametrize("config", [{"a": {"b": 1}}, {"a": [1, 2, 3]}])
def test_set_flag_recursively(config: Any) -> None:
c = OmegaConf.create(config)
assert not c._get_flag("readonly")
assert not c._get_flag("struct")
c.a._set_flag(["readonly", "struct"], [True, True], recursive=False)
assert c.a._get_flag("readonly")
assert c.a._get_flag("struct")
assert not c._get_flag("readonly")
assert not c._get_flag("struct")

c._set_flag("readonly", False, recursive=True)
assert not c.a._get_flag("readonly")
assert c.a._get_flag("struct")

c._set_flag("struct", False, recursive=True)
assert not c.a._get_flag("struct")


@mark.parametrize("no_deepcopy_set_nodes", [True, False])
@mark.parametrize("node", [20, {"b": 10}, [1, 2]])
def test_get_flag_after_dict_assignment(no_deepcopy_set_nodes: bool, node: Any) -> None:
Expand Down