Commit ae9b4f76 authored by Matias Guijarro's avatar Matias Guijarro

common/utils.py: added 'deep_update' function

Update values of nested dict of varying depth
parent 6c3e7929
......@@ -606,3 +606,19 @@ class autocomplete_property(property):
"""
pass
def deep_update(source, overrides):
"""
Update a nested dictionary or similar mapping.
Modify ``source`` in place.
Copied from https://stackoverflow.com/questions/3232943/update-value-of-a-nested-dictionary-of-varying-depth/32357112#32357112
"""
for key, value in overrides.items():
if isinstance(value, collections.abc.Mapping) and value:
returned = deep_update(source.get(key, {}), value)
source[key] = returned
else:
source[key] = overrides[key]
return source
......@@ -23,7 +23,7 @@ from bliss.common.event import connect, send, disconnect
from bliss.common.cleanup import error_cleanup, axis as cleanup_axis, capture_exceptions
from bliss.common.greenlet_utils import KillMask
from bliss.common.plot import get_flint, CurvePlot, ImagePlot
from bliss.common.utils import periodic_exec
from bliss.common.utils import periodic_exec, deep_update
from .scan_meta import get_user_scan_meta
from bliss.common.utils import Statistics, Null
from bliss.config.conductor import client
......@@ -961,7 +961,7 @@ class Scan:
# make sure that 'positioners' entry is not updated
tmp_dict["instrument"].pop("positioners")
tmp_dict["instrument"].pop("positioners_dial")
nested_dict_update(self._scan_info, tmp_dict)
deep_update(self._scan_info, tmp_dict)
# update scan_info in redis
self.node._info.update(self.scan_info)
......@@ -1124,24 +1124,3 @@ class Scan:
gevent.joinall(preset_tasks, raise_error=True)
finally:
gevent.killall(preset_tasks)
# there should be something like this in the python standard lib, but I didn't find it...
# ... suggestions welcome
# ... in case we keep our own implementation ... where should it be? in utils?
def nested_dict_update(dict_to_update, newdict):
present_key_value_pairs = [
(key, value) for (key, value) in newdict.items() if key in dict_to_update.keys()
]
new_key_value_pairs = [
(key, value)
for (key, value) in newdict.items()
if not key in dict_to_update.keys()
]
for key, new_value in present_key_value_pairs:
if isinstance(new_value, dict):
dict_to_update[key] = nested_dict_update(dict_to_update[key], new_value)
else:
dict_to_update[key] = new_value
dict_to_update.update(dict(new_key_value_pairs))
return dict_to_update
......@@ -7,8 +7,8 @@
from bliss import setup_globals
from bliss.common.standard import wa, wm, sta, stm
from bliss.shell.cli import repl
from bliss.common.utils import deep_update
repl.ERROR_REPORT.expert_mode = True
......@@ -168,3 +168,30 @@ def test_stm_exception(beacon, capsys):
errmsg = "RuntimeError: Error on motor 'bad': BAD POSITION\n"
assert captured.err[-len(errmsg) :] == errmsg
def test_deep_update():
source = {"hello1": 1}
overrides = {"hello2": 2}
deep_update(source, overrides)
assert source == {"hello1": 1, "hello2": 2}
source = {"hello": "to_override"}
overrides = {"hello": "over"}
deep_update(source, overrides)
assert source == {"hello": "over"}
source = {"hello": {"value": "to_override", "no_change": 1}}
overrides = {"hello": {"value": "over"}}
deep_update(source, overrides)
assert source == {"hello": {"value": "over", "no_change": 1}}
source = {"hello": {"value": "to_override", "no_change": 1}}
overrides = {"hello": {"value": {}}}
deep_update(source, overrides)
assert source == {"hello": {"value": {}, "no_change": 1}}
source = {"hello": {"value": {}, "no_change": 1}}
overrides = {"hello": {"value": 2}}
deep_update(source, overrides)
assert source == {"hello": {"value": 2, "no_change": 1}}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment