Fix parameter interpolation across merged lists
This was easier than thought. The problem:
If two lists are merged (extended), and those lists use RefValues,
then expansion (interpolation) won't work. The reason is that the
DictPath includes the list indices, e.g. apt_repos:0:uri and those
indices start at 0 for every list. Now, when two lists are merged,
the reference to the base RefValue (in Parameters._occurrences) is
overwritten by the RefValue of the mergee, causing it to not get
expanded later.
Instead of just updating self._occurrences, we can populate it as we
update scalars, and on extending lists, we can add an offset to the
index in the path of the mergee.
Still: DictPath should go. One day…
Closes: #13
Signed-off-by: martin f. krafft <madduck@madduck.net>
diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst
index 1bd522a..a5b95e4 100644
--- a/doc/source/changelog.rst
+++ b/doc/source/changelog.rst
@@ -5,6 +5,8 @@
========= ========== ========================================================
Version Date Changes
========= ========== ========================================================
+ * Fix parameter interpolation across merged lists
+ (closes: #13).
* Caching of classes for performance reasons, especially
during the inventory runs
* yaml_fs: nodes may be defined in subdirectories
diff --git a/reclass/datatypes/parameters.py b/reclass/datatypes/parameters.py
index 078a247..7f0f33a 100644
--- a/reclass/datatypes/parameters.py
+++ b/reclass/datatypes/parameters.py
@@ -70,30 +70,46 @@
return self._base.copy()
def _update_scalar(self, cur, new, path):
- if self.delimiter is None or not isinstance(new, types.StringTypes):
+ if self.delimiter is None or not isinstance(new, (types.StringTypes,
+ RefValue)):
+ # either there is no delimiter defined (and hence no references
+ # are being used), or the new value is not a string (and hence
+ # cannot be turned into a RefValue), and not a RefValue. We can
+ # shortcut and just return the new scalar
return new
+ elif isinstance(new, RefValue):
+ # the new value is (already) a RefValue, so we need not touch it
+ # at all
+ ret = new
+
else:
+ # the new value is a string, let's see if it contains references,
+ # by way of wrapping it in a RefValue and querying the result
ret = RefValue(new, self.delimiter)
if not ret.has_references():
# do not replace with RefValue instance if there are no
- # references
+ # references, i.e. discard the RefValue in ret, just return
+ # the new value
return new
- # finally, keep a reference to the RefValue instance we just
- # created, in a dict indexed by the dictionary path, instead of
- # just a list. The keys are required to resolve dependencies
- # during interpolation
- self._occurrences[path] = ret
- return ret
+ # So we now have a RefValue. Let's, keep a reference to the instance
+ # we just created, in a dict indexed by the dictionary path, instead
+ # of just a list. The keys are required to resolve dependencies during
+ # interpolation
+ self._occurrences[path] = ret
+ return ret
def _extend_list(self, cur, new, path):
if isinstance(cur, list):
ret = cur
+ offset = len(cur)
else:
ret = [cur]
+ offset = 1
+
for i in xrange(len(new)):
- ret.append(self._merge_recurse(None, new[i], path.new_subpath(i)))
+ ret.append(self._merge_recurse(None, new[i], path.new_subpath(offset + i)))
return ret
def _merge_dict(self, cur, new, path):
@@ -140,7 +156,6 @@
elif isinstance(other, self.__class__):
self._base = self._merge_recurse(self._base, other._base,
None)
- self._occurrences.update(other._occurrences)
else:
raise TypeError('Cannot merge %s objects into %s' % (type(other),