Commit cc1d44c8 authored by Sebastien Petitdemange's avatar Sebastien Petitdemange Committed by Antonia Beteva
Browse files

config: replace object with reference.

+ fixed save for copied node
parent 119ca709
......@@ -91,6 +91,50 @@ class BlissSafeYamlLoader(
BlissYamlResolver.__init__(self)
def _replace_object_with_ref(obj):
try:
obj_name = obj.name
except AttributeError:
return False, obj
else:
config = get_config()
if config._name2instance.get(obj_name):
return True, f"${obj_name}"
else:
return False, obj
def _replace_list_node_with_ref(node_list):
final_list = []
for sub_node in node_list:
if isinstance(sub_node, list):
sub_list = _replace_list_node_with_ref(sub_node)
final_list.append(sub_list)
elif isinstance(sub_node, dict):
_replace_node_with_ref(sub_node)
final_list.append(sub_node)
else:
replaced, new_value = _replace_object_with_ref(sub_node)
final_list.append(new_value if replaced else sub_node)
return final_list
def _replace_node_with_ref(node):
"""
replace all object with theirs references
"""
for key, value in list(node.items()):
replaced, new_value = _replace_object_with_ref(value)
if replaced:
node[key] = new_value
continue
if isinstance(value, dict):
_replace_node_with_ref(value)
elif isinstance(value, list):
node[key] = _replace_list_node_with_ref(value)
def get_config(base_path="", timeout=3.):
"""
Return configuration from bliss configuration server
......@@ -198,7 +242,8 @@ class Node(dict):
~bliss.config.static.Node: the Node file object where this Node is
defined
"""
filename = self._config._node2file.get(self)
node = self if not hasattr(self, "_copied_from") else self._copied_from
filename = self._config._node2file.get(node)
if filename is not None:
return self, filename
elif self._parent is not None:
......@@ -243,7 +288,18 @@ class Node(dict):
"""
Saves the Node configuration persistently in the server
"""
# Get the original node, synchronize it with
# the copied one
parent, filename = self.get_node_filename()
if hasattr(self, "_copied_from"):
# first copy to not modify
copied_node = self.deep_copy()
_replace_node_with_ref(copied_node)
self._copied_from.clear()
self._copied_from.update(copied_node)
else:
_replace_node_with_ref(self)
if filename is None:
return # Memory
nodes_2_save = self._config._file2node[filename]
......@@ -262,10 +318,18 @@ class Node(dict):
node = Node()
node._config = self._config
node._parent = self._parent
# keep source node in case of saving
if hasattr(self, "_copied_from"):
node._copied_from = self._copied_from
else:
node._copied_from = self
for key, value in self.items():
if isinstance(value, Node):
child_node = value.deep_copy()
node[key] = child_node
elif isinstance(value, dict):
child_node = value.copy()
node[key] = child_node
elif isinstance(value, list):
new_list = Node._copy_list(value)
node[key] = new_list
......@@ -298,6 +362,8 @@ class Node(dict):
if isinstance(v, Node):
new_node = v.deep_copy() if not dict_mode else v.to_dict()
new_list.append(new_node)
elif isinstance(v, dict):
new_list.append(v.copy())
elif isinstance(v, list):
child_list = Node._copy_list(v, dict_mode=dict_mode)
new_list.append(child_list)
......
......@@ -6,14 +6,26 @@
# Distributed under the GNU LGPLv3. See LICENSE for more info.
from bliss.config.conductor import client
from bliss.config.plugins.utils import replace_reference_by_object
import pytest
import sys, os
def test_config_save(beacon, beacon_directory):
test_file_path = os.path.join(beacon_directory, "read_write.yml")
rw_cfg = beacon.get_config("rw_test")
test_file_contents = client.get_text_file("read_write.yml")
@pytest.mark.parametrize(
"file_name, node_name, copy",
[
["read_write.yml", "rw_test", False],
["read_write_2.yml", "rw_test_2", False],
["read_write.yml", "rw_test", True],
["read_write_2.yml", "rw_test_2", True],
],
)
def test_config_save(beacon, beacon_directory, file_name, node_name, copy):
test_file_path = os.path.join(beacon_directory, file_name)
rw_cfg = beacon.get_config(node_name)
if copy:
rw_cfg = rw_cfg.deep_copy()
test_file_contents = client.get_text_file(file_name)
with open(test_file_path, "r") as f:
assert f.read() == test_file_contents
......@@ -29,7 +41,7 @@ def test_config_save(beacon, beacon_directory):
beacon.reload()
rw_cfg2 = beacon.get_config("rw_test")
rw_cfg2 = beacon.get_config(node_name)
assert id(rw_cfg2) != id(rw_cfg)
assert rw_cfg2["one"][0]["red"] == "strawberry"
......@@ -106,3 +118,32 @@ def test_yaml_boolean(beacon):
assert m["outputs"][0]["ON"] == 1
assert m["outputs"][0]["OFF"] == 0
def test_config_save_reference(beacon, beacon_directory):
file_name = "read_write_2.yml"
node_name = "rw_test_2"
rw_cfg = beacon.get_config(node_name).deep_copy()
replace_reference_by_object(beacon, rw_cfg, dict())
diode = beacon.get("diode")
diode2 = beacon.get("diode2")
diode3 = beacon.get("diode3")
rw_cfg["test_list"].append(diode2)
rw_cfg["dict_list"].append({"cnt_channel": "c", "instance": diode})
rw_cfg.save()
beacon.reload()
rw_cfg2 = beacon.get_config(node_name)
replace_reference_by_object(beacon, rw_cfg2, dict())
assert id(rw_cfg2) != id(rw_cfg)
assert len(rw_cfg2["test_list"]) == len(rw_cfg["test_list"])
assert [x.name for x in rw_cfg2["test_list"]] == [
x.name for x in rw_cfg["test_list"]
]
assert len(rw_cfg2["dict_list"]) == len(rw_cfg["dict_list"])
assert (
rw_cfg2["dict_list"][2]["instance"].name
== rw_cfg["dict_list"][2]["instance"].name
)
name: rw_test_2
one:
- pink: martini
red: apples
blue: berry
two:
- a: true
b: false
c: null
test_list:
- $diode
dict_list:
- cnt_channel: a
instance: $diode2
- cnt_channel: b
instance: $diode3
\ No newline at end of file
Supports Markdown
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