use better algorithm for individual export evaluation
diff --git a/reclass/datatypes/exports.py b/reclass/datatypes/exports.py
index 91bc149..375ea0b 100644
--- a/reclass/datatypes/exports.py
+++ b/reclass/datatypes/exports.py
@@ -10,6 +10,7 @@
from reclass.errors import ResolveError
from reclass.values.value import Value
from reclass.values.valuelist import ValueList
+from reclass.utils.dictpath import DictPath
class Exports(Parameters):
@@ -43,32 +44,40 @@
del self._unrendered[path]
def interpolate_single_from_external(self, external, query):
- paths = {}
for r in query.get_inv_references():
- paths[r] = True
+ self._interpolate_single_path_from_external(r, external, query)
- rendered = {}
- while len(paths) > 0:
- path, v = paths.iteritems().next()
- rendpath = path.deepest_match_in(self._base)
- if rendpath in rendered:
- del paths[path]
- continue
- if rendpath.exists_in(self._base) and rendpath in self._unrendered:
- value = rendpath.get_value(self._base)
+ def _interpolate_single_path_from_external(self, mainpath, external, query):
+ required = self._get_required_paths(mainpath)
+ while len(required) > 0:
+ while len(required) > 0:
+ path, v = required.iteritems().next()
+ value = path.get_value(self._base)
if isinstance(value, (Value, ValueList)):
try:
- external._interpolate_references(rendpath, value, None)
- new = self._interpolate_render_from_external(external._base, rendpath, value)
- rendpath.set_value(self._base, new)
+ external._interpolate_references(path, value, None)
+ new = self._interpolate_render_from_external(external._base, path, value)
+ path.set_value(self._base, new)
except ResolveError as e:
if query.ignore_failed_render():
- rendpath.delete(self._base)
+ path.delete(self._base)
else:
raise
- rendered[rendpath] = True
- paths.pop(rendpath, None)
- self._unrendered.pop(rendpath, None)
+ del required[path]
+ del self._unrendered[path]
+ required = self._get_required_paths(mainpath)
+
+ def _get_required_paths(self, mainpath):
+ paths = {}
+ path = DictPath(self._settings.delimiter)
+ for i in mainpath.key_parts():
+ path.new_subpath(i)
+ if path in self._unrendered:
+ paths[path] = True
+ for i in self._unrendered:
+ if mainpath.is_ancestor_of(i) or mainpath == i:
+ paths[i] = True
+ return paths
def _interpolate_render_from_external(self, context, path, value):
try:
diff --git a/reclass/datatypes/tests/test_entity.py b/reclass/datatypes/tests/test_entity.py
index fde8067..1af9caa 100644
--- a/reclass/datatypes/tests/test_entity.py
+++ b/reclass/datatypes/tests/test_entity.py
@@ -187,6 +187,28 @@
self.assertDictEqual(node3_parameters.as_dict(), res_params)
self.assertDictEqual(inventory, res_inv)
+ def test_exports_multiple_nodes(self):
+ node1_exports = Exports({'a': '${a}'}, SETTINGS, '')
+ node1_parameters = Parameters({'name': 'node1', 'a': { 'test': '${b}' }, 'b': 1, 'exp': '$[ exports:a ]'}, SETTINGS, '')
+ node1_entity = Entity(SETTINGS, classes=None, applications=None, parameters=node1_parameters, exports=node1_exports)
+ node2_exports = Exports({'a': '${a}'}, SETTINGS, '')
+ node2_parameters = Parameters({'name': 'node2', 'a': { 'test': '${b}' }, 'b': 2 }, SETTINGS, '')
+ node2_entity = Entity(SETTINGS, classes=None, applications=None, parameters=node2_parameters, exports=node2_exports)
+ node1_entity.initialise_interpolation()
+ node2_entity.initialise_interpolation()
+ queries = node1_entity.parameters.get_inv_queries()
+ for p, q in queries:
+ node1_entity.interpolate_single_export(q)
+ node2_entity.interpolate_single_export(q)
+ res_inv = {'node1': {'a': {'test': 1}}, 'node2': {'a': {'test': 2}}}
+ res_params = {'a': {'test': 1}, 'b': 1, 'name': 'node1', 'exp': {'node1': {'test': 1}, 'node2': {'test': 2}}}
+ inventory = {}
+ inventory['node1'] = node1_entity.exports.as_dict()
+ inventory['node2'] = node2_entity.exports.as_dict()
+ node1_entity.interpolate(inventory)
+ self.assertDictEqual(node1_parameters.as_dict(), res_params)
+ self.assertDictEqual(inventory, res_inv)
+
def test_exports_with_nested_references(self):
inventory = {'node1': {'alpha': {'a': 1, 'b': 2}}, 'node2': {'alpha': {'a': 3, 'b': 4}}}
node3_exports = Exports({'alpha': '${alpha}'}, SETTINGS, '')
diff --git a/reclass/utils/dictpath.py b/reclass/utils/dictpath.py
index eae57a1..28a738f 100644
--- a/reclass/utils/dictpath.py
+++ b/reclass/utils/dictpath.py
@@ -140,6 +140,14 @@
def delete(self, base):
del self._get_innermost_container(base)[self._get_key()]
+ def is_ancestor_of(self, other):
+ if len(other._parts) <= len(self._parts):
+ return False
+ for i in range(len(self._parts)):
+ if other._parts[i] != self._parts[i]:
+ return False
+ return True
+
def exists_in(self, container):
item = container
for i in self._parts:
@@ -158,21 +166,21 @@
return False
return True
- def deepest_match_in(self, container):
- match = DictPath(self._delim)
- item = container
- for i in self._parts:
- if isinstance(item, (dict, list)):
- if i in item:
- if isinstance(item, dict):
- item = item[i]
- elif isinstance(container, list):
- item = item[int(i)]
- match = match.new_subpath(i)
- else:
- return match
- else:
- if item == self._parts[-1]:
- match = match.new_subpath(i)
- return match
- return match
+# def deepest_match_in(self, container):
+# match = DictPath(self._delim)
+# item = container
+# for i in self._parts:
+# if isinstance(item, (dict, list)):
+# if i in item:
+# if isinstance(item, dict):
+# item = item[i]
+# elif isinstance(container, list):
+# item = item[int(i)]
+# match = match.new_subpath(i)
+# else:
+# return match
+# else:
+# if item == self._parts[-1]:
+# match = match.new_subpath(i)
+# return match
+# return match