Prevent interpolate() from overwriting values updated during merge
When during a merge, a RefValue instance (a scalar) gets overwritten by
a non RefValue scalar, then remove the record of the occurrence of the
overwritten RefValue. Otherwise, interpolate() will later
unconditionally overwrite the new value with the interpolated reference.
Arguably, interpolet() should check whether the value it's overwriting
is a RefValue instance, but that's left for another day.
Signed-off-by: martin f. krafft <madduck@madduck.net>
diff --git a/reclass/datatypes/parameters.py b/reclass/datatypes/parameters.py
index 9f5b9aa..37419fc 100644
--- a/reclass/datatypes/parameters.py
+++ b/reclass/datatypes/parameters.py
@@ -70,6 +70,13 @@
return self._base.copy()
def _update_scalar(self, cur, new, path):
+ if isinstance(cur, RefValue) and path in self._occurrences:
+ # If the current value already holds a RefValue, we better forget
+ # the occurrence, or else interpolate() will later overwrite
+ # unconditionally. If the new value is a RefValue, the occurrence
+ # will be added again further on
+ del self._occurrences[path]
+
if self.delimiter is None or not isinstance(new, (types.StringTypes,
RefValue)):
# either there is no delimiter defined (and hence no references
diff --git a/reclass/datatypes/tests/test_parameters.py b/reclass/datatypes/tests/test_parameters.py
index 18fc5dd..f58056d 100644
--- a/reclass/datatypes/tests/test_parameters.py
+++ b/reclass/datatypes/tests/test_parameters.py
@@ -127,6 +127,12 @@
self.assertIn(mock.call(key), b1.get.call_args_list)
self.assertIn(mock.call(key, value), b1.__setitem__.call_args_list)
+ def test_stray_occurrence_overwrites_during_interpolation(self):
+ p1 = Parameters({'r' : mock.sentinel.ref, 'b': '${r}'})
+ p2 = Parameters({'b' : mock.sentinel.goal})
+ p1.merge(p2)
+ p1.interpolate()
+ self.assertEqual(p1.as_dict()['b'], mock.sentinel.goal)
class TestParametersNoMock(unittest.TestCase):