-
Notifications
You must be signed in to change notification settings - Fork 116
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
Add force_add to OmegaConf.update(), effectively using open_dict for all nodes along the path #665
Changes from all commits
6567068
3d26236
431c095
d7cf7fb
02a0c6b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
force_add flag added to OmegaConf.update(), ensuring that the path is created even if it will result in insertion of new values into struct nodes. |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -727,7 +727,12 @@ def select( | |||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
@staticmethod | ||||||||||||||||||||||||||||||||||||||
def update( | ||||||||||||||||||||||||||||||||||||||
cfg: Container, key: str, value: Any = None, merge: Optional[bool] = None | ||||||||||||||||||||||||||||||||||||||
cfg: Container, | ||||||||||||||||||||||||||||||||||||||
key: str, | ||||||||||||||||||||||||||||||||||||||
value: Any = None, | ||||||||||||||||||||||||||||||||||||||
*, | ||||||||||||||||||||||||||||||||||||||
merge: Optional[bool] = None, | ||||||||||||||||||||||||||||||||||||||
odelalleau marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||
force_add: bool = False, | ||||||||||||||||||||||||||||||||||||||
) -> None: | ||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||
Updates a dot separated key sequence to a value | ||||||||||||||||||||||||||||||||||||||
|
@@ -739,7 +744,8 @@ def update( | |||||||||||||||||||||||||||||||||||||
:param merge: If value is a dict or a list, True for merge, False for set. | ||||||||||||||||||||||||||||||||||||||
True to merge | ||||||||||||||||||||||||||||||||||||||
False to set | ||||||||||||||||||||||||||||||||||||||
None (default) : deprecation warning and default to False | ||||||||||||||||||||||||||||||||||||||
None (default): deprecation warning and default to False | ||||||||||||||||||||||||||||||||||||||
:param force_add: insert the entire path regardless of Struct flag or Structured Config nodes. | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It may be surprising for (if it's changed, it may be easier to rename it into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reported problem is specifically about adding a field to a Structured Config. read-only is used as a mechanism to protect against changes to nodes here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. I feel like it may be possible to have a version of |
||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
if merge is None: | ||||||||||||||||||||||||||||||||||||||
|
@@ -757,12 +763,14 @@ def update( | |||||||||||||||||||||||||||||||||||||
split = split_key(key) | ||||||||||||||||||||||||||||||||||||||
root = cfg | ||||||||||||||||||||||||||||||||||||||
for i in range(len(split) - 1): | ||||||||||||||||||||||||||||||||||||||
k = split[i] | ||||||||||||||||||||||||||||||||||||||
# if next_root is a primitive (string, int etc) replace it with an empty map | ||||||||||||||||||||||||||||||||||||||
next_root, key_ = _select_one(root, k, throw_on_missing=False) | ||||||||||||||||||||||||||||||||||||||
if not isinstance(next_root, Container): | ||||||||||||||||||||||||||||||||||||||
root[key_] = {} | ||||||||||||||||||||||||||||||||||||||
root = root[key_] | ||||||||||||||||||||||||||||||||||||||
struct_override = False if force_add else root._get_node_flag("struct") | ||||||||||||||||||||||||||||||||||||||
with flag_override(root, "struct", struct_override): | ||||||||||||||||||||||||||||||||||||||
k = split[i] | ||||||||||||||||||||||||||||||||||||||
# if next_root is a primitive (string, int etc) replace it with an empty map | ||||||||||||||||||||||||||||||||||||||
next_root, key_ = _select_one(root, k, throw_on_missing=False) | ||||||||||||||||||||||||||||||||||||||
if not isinstance(next_root, Container): | ||||||||||||||||||||||||||||||||||||||
root[key_] = {} | ||||||||||||||||||||||||||||||||||||||
root = root[key_] | ||||||||||||||||||||||||||||||||||||||
Comment on lines
+766
to
+773
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optimization by only calling
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code here will not apply cleanly, (see syntax error in 775). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, oops, looks like my mouse select skills need improving. I just added the missing bracket. |
||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
last = split[-1] | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
|
@@ -774,22 +782,24 @@ def update( | |||||||||||||||||||||||||||||||||||||
if isinstance(root, ListConfig): | ||||||||||||||||||||||||||||||||||||||
last_key = int(last) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
if merge and (OmegaConf.is_config(value) or is_primitive_container(value)): | ||||||||||||||||||||||||||||||||||||||
assert isinstance(root, BaseContainer) | ||||||||||||||||||||||||||||||||||||||
node = root._get_node(last_key) | ||||||||||||||||||||||||||||||||||||||
if OmegaConf.is_config(node): | ||||||||||||||||||||||||||||||||||||||
assert isinstance(node, BaseContainer) | ||||||||||||||||||||||||||||||||||||||
node.merge_with(value) | ||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
if OmegaConf.is_dict(root): | ||||||||||||||||||||||||||||||||||||||
assert isinstance(last_key, str) | ||||||||||||||||||||||||||||||||||||||
root.__setattr__(last_key, value) | ||||||||||||||||||||||||||||||||||||||
elif OmegaConf.is_list(root): | ||||||||||||||||||||||||||||||||||||||
assert isinstance(last_key, int) | ||||||||||||||||||||||||||||||||||||||
root.__setitem__(last_key, value) | ||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||
assert False | ||||||||||||||||||||||||||||||||||||||
struct_override = False if force_add else root._get_node_flag("struct") | ||||||||||||||||||||||||||||||||||||||
with flag_override(root, "struct", struct_override): | ||||||||||||||||||||||||||||||||||||||
Comment on lines
+785
to
+786
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A similar optimization could be done to avoid overriding for no reason. Something like:
Suggested change
However |
||||||||||||||||||||||||||||||||||||||
if merge and (OmegaConf.is_config(value) or is_primitive_container(value)): | ||||||||||||||||||||||||||||||||||||||
assert isinstance(root, BaseContainer) | ||||||||||||||||||||||||||||||||||||||
node = root._get_node(last_key) | ||||||||||||||||||||||||||||||||||||||
if OmegaConf.is_config(node): | ||||||||||||||||||||||||||||||||||||||
assert isinstance(node, BaseContainer) | ||||||||||||||||||||||||||||||||||||||
node.merge_with(value) | ||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
if OmegaConf.is_dict(root): | ||||||||||||||||||||||||||||||||||||||
assert isinstance(last_key, str) | ||||||||||||||||||||||||||||||||||||||
root.__setattr__(last_key, value) | ||||||||||||||||||||||||||||||||||||||
elif OmegaConf.is_list(root): | ||||||||||||||||||||||||||||||||||||||
assert isinstance(last_key, int) | ||||||||||||||||||||||||||||||||||||||
root.__setitem__(last_key, value) | ||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||
assert False | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
@staticmethod | ||||||||||||||||||||||||||||||||||||||
def to_yaml(cfg: Any, *, resolve: bool = False, sort_keys: bool = False) -> str: | ||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a benchmark to verify the excessive flag overrides are not causing performance regression.
The concern is that the recursive flags cache invalidation when a flag is set will slow things down.
It does not seem to cause a significant slowdown for a large dict_config here though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's too much to worry here because in the worst case it would only invalidate the whole config cache once: after it's cleared, subsequent cache invalidation wouldn't trickle down anymore.
That being said, I'm not sure if
large_dict_config
actually comes from a cache set on all config nodes, which would be needed to evaluate this worst case scenario. If that's not the case then the benchmark isn't very relevant (at least to evaluate the impact of cache invalidation on performance).I also suggested an optimization that should reduce the amount of cache invalidation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I didn't notice the gating in _invalidate_flags_cache.
Good point about the benchmark not doing much.
I fixed the benchmark to populate the flags cache on all nodes. We don't have a baseline though.
Overall I am not too concerned about relative performance here though, at 400us per call on the big config this is plenty fast enough.