Repository: incubator-ariatosca
Updated Branches:
  refs/heads/ARIA-258-Convert-runtime-properties-to-attributes e2f5e25a8 -> 
ff3d9647d


wip...


Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/ff3d9647
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/ff3d9647
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/ff3d9647

Branch: refs/heads/ARIA-258-Convert-runtime-properties-to-attributes
Commit: ff3d9647d88d3c5cb93c1eb27a5d5c28af73e869
Parents: e2f5e25
Author: max-orlov <[email protected]>
Authored: Sun May 21 21:24:42 2017 +0300
Committer: max-orlov <[email protected]>
Committed: Sun May 21 21:24:42 2017 +0300

----------------------------------------------------------------------
 aria/orchestrator/context/common.py          | 188 +++++++++++++---------
 tests/orchestrator/context/test_operation.py |  53 +++---
 2 files changed, 133 insertions(+), 108 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/ff3d9647/aria/orchestrator/context/common.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/common.py 
b/aria/orchestrator/context/common.py
index 46e315f..e1dbf5f 100644
--- a/aria/orchestrator/context/common.py
+++ b/aria/orchestrator/context/common.py
@@ -16,7 +16,7 @@
 """
 A common context for both workflow and operation
 """
-
+import copy
 import logging
 import collections
 from contextlib import contextmanager
@@ -202,124 +202,146 @@ class BaseContext(object):
         self.model.log._engine.dispose()
 
 
-class _InstrumentedCollection(object):
-    def __init__(self, parent, model, nested_key=None, **kwargs):
+class _InstrumentedCollection(dict):
+    def __init__(self, parent, model, actor=None, field_name=None, 
nested_key=None, **kwargs):
         super(_InstrumentedCollection, self).__init__(**kwargs)
+
         self._parent = parent
         self._model = model
-        self._attr_cls = self._model.parameter.model_cls
-        self._nested_key = nested_key or None
+        self._item_cls = self._model.parameter.model_cls
+        self._nested_key = nested_key
+
+        # Actor is not None only at the top level, where it should be updated
+        self._actor = actor
+        self._field_name = field_name
+        if self._actor:
+            # Only the root parent has an _actor
+            self._init_from_parent()
 
     def __getitem__(self, key):
         if self._nested_key is None:
             value = self._parent[key].value
         elif isinstance(self, dict):
             value = dict.__getitem__(self, key)
-        elif isinstance(self, list):
-            value = list.__getitem__(self, key)
         else:
             raise BaseException()
 
-        if isinstance(value, list):
-            return _InstrumentedList(self, self._model, key, _list=value)
+        if isinstance(value, (list, _InstrumentedList)):
+            return _InstrumentedList(self, self._model, nested_key=key, 
_list=value)
         elif isinstance(value, dict):
-            return _InstrumentedDict(self, self._model, key, **value)
+            return _InstrumentedDict(self, self._model, nested_key=key, 
**value)
         else:
             return value
 
-
-class _InstrumentedDict(_InstrumentedCollection, dict):
-
-    def __setitem__(self, i, y):
-        super(_InstrumentedDict, self).__setitem__(i,
-                                                   y.value if isinstance(y, 
self._attr_cls) else y)
+    def _set(self, i, y):
+        self._insert(i, y)
         if self._nested_key is None:
             nested_key = i
-            if not isinstance(y, self._attr_cls):
-                y = self._attr_cls.wrap(i, y)
+            if not isinstance(y, self._item_cls):
+                y = self._item_cls.wrap(i, y)
             self._model.parameter.put(y)
         else:
             nested_key = self._nested_key
 
         self._update_parent(nested_key, i, y)
+        return y
+
+    def _init_from_parent(self):
+        for key, value in self._parent.items():
+            self._insert(key, value)
+
+    def _insert(self, key, value):
+        value = value.value if isinstance(value, self._item_cls) else value
+        super(_InstrumentedCollection, self).__setitem__(key, value)
+
+    def _update_parent(self, nested_key, key, value):
+        if isinstance(self._parent, _InstrumentedCollection):
+            self._parent._update_from_child(nested_key, {key: value})
+        else:
+            if self._nested_key is not None:
+                self._parent[nested_key] = {key: value}
+            else:
+                self._parent[key] = value
+
+    def _update_from_child(self, nested_key, value):
+        self[nested_key] = value
+        if isinstance(value, self._item_cls):
+            # We are the the top level
+            getattr(self._actor, self._field_name)[nested_key] = value
+            self._model.parameter.update(value)
 
-    def items(self):
-        items = super(_InstrumentedDict, self).items()
-        return ((key, value.value if isinstance(value, self._attr_cls) else 
value)
-                for key, value in items)
+    def _update_actor(self, key, value):
+        raise NotImplementedError
 
-    def values(self):
-        for _, value in self.items():
-            yield value
+    def __setitem__(self, key, value):
+        value = self._set(key, value)
 
+        if self._actor:
+            self._update_actor(key, value)
+
+        if isinstance(value, self._item_cls):
+            self._model.parameter.update(value)
+
+
+class _InstrumentedDict(_InstrumentedCollection):
+    """
+    Dict implementation for instrumented collection
+    """
     def update(self, E=None, **F):
         dict_ = E or {}
         dict_.update(F.copy())
         for key, value in dict_.items():
             self[key] = value
 
-    def _update_parent(self, nested_key, key, value):
-        if isinstance(self._parent.get(self._nested_key), self._attr_cls):
-            self._parent[nested_key].value[key] = value
-        else:
-            if nested_key == key:
-                self._parent[nested_key] = value
-            else:
-                self._parent[nested_key] = {key: value}
-
     def clear(self):
         self._parent.get(self._nested_key, {}).clear()
         dict.clear(self)
 
+    def _update_actor(self, key, value):
+        getattr(self._actor, self._field_name)[key] = value
+
 
-class _InstrumentedList(_InstrumentedCollection, list):
+class _InstrumentedList(_InstrumentedDict):
+    """
+    List implementation of instrumented collection
+    """
+    def __init__(self, parent, *args, **kwargs):
+        list_ = kwargs.pop('_list', [])
+        if isinstance(parent, list):
+            parent = list(enumerate(parent))
+        super(_InstrumentedList, self).__init__(parent, *args, **kwargs)
+        for item in list_:
+            self.append(item)
+
+    def __iter__(self):
+        for _, item in sorted(self.items(), cmp=lambda item: item[0]):
+            yield item
+
+    def _update_actor(self, key, value):
+        field = getattr(self._actor, self._field_name)
+        if key < len(field):
+            field[key] = value
+        else:
+            field.insert(key, value)
 
-    def __init__(self, *args, **kwargs):
-        super(_InstrumentedList, self).__init__(*args, **kwargs)
-        list.__init__(self, kwargs.pop('_list', []))
+    def _init_from_parent(self):
+        for item in self._parent:
+            self.append(item)
 
-    def append(self, p_object):
-        self.insert(len(self), p_object)
+    def append(self, item):
+        self._set(len(self), item)
 
-    def insert(self, index, p_object):
-        list.insert(self, index, p_object)
-        if self._nested_key is not None:
-            attribute = self._update_attr(index, p_object)
-            self._model.parameter.update(attribute)
-        elif len(self._parent) > index:
-            self._parent[index].value = p_object
-            self._model.parameter.update(self._parent[index])
+    def _update_parent(self, nested_key, key, value):
+        if isinstance(self._parent, _InstrumentedCollection):
+            self._parent._update_from_child(nested_key, {key: value})
         else:
-            attr = p_object if isinstance(p_object, self._attr_cls) else 
self._attr_cls.wrap(index, p_object)
-            self._parent.insert(index, attr)
-            self._model.parameter.put(self._parent)
-
-    def __setitem__(self, index, value):
-        return self.insert(index, value)
-
-    def _update_attr(self, index, value):
-        attribute = current = self._parent[self._nested_key[0]]
-        for i in self._nested_key[1:]:
-            current = current[i]
-        if isinstance(current, self._attr_cls):
-            if isinstance(current.value, list):
-                current.value.insert(index, value)
+            if self._nested_key is not None:
+                self._parent[nested_key] = {key: value}
             else:
-                current.value[index] = value
-        else:
-            current[index] = value
-
-        if isinstance(attribute.value, dict):
-            value = attribute.value.copy()
-            attribute.value.clear()
-            attribute.value = value
-        elif isinstance(attribute.value, list):
-            attribute.value[:] = current.value
+                self._parent.insert(key, value)
 
-        return attribute
-
-    def __eq__(self, other):
-        return self._parent.__eq__(other)
+    def __repr__(self):
+        return repr(list(self))
 
 
 class InstrumentCollection(object):
@@ -343,9 +365,15 @@ class InstrumentCollection(object):
         def _wrapper(func_self, *args, **kwargs):
             self._actor = func(func_self, *args, **kwargs)
             field = getattr(self._actor, self._field_name)
-            if isinstance(field, dict):
-                setattr(self, self._field_name, _InstrumentedDict(field, 
func_self.model))
-            elif isinstance(field, list):
-                setattr(self, self._field_name, _InstrumentedList(field, 
func_self.model))
+
+            # Preserve the original value
+            setattr(self, '_{0}'.format(self._field_name), field)
+
+            # set instrumented value
+            inst_cls = _InstrumentedDict if isinstance(field, dict) else 
_InstrumentedList
+            setattr(
+                self,
+                self._field_name,
+                inst_cls(field, func_self.model, actor=self._actor, 
field_name=self._field_name))
             return self
         return _wrapper

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/ff3d9647/tests/orchestrator/context/test_operation.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_operation.py 
b/tests/orchestrator/context/test_operation.py
index 669c2d5..72b8efe 100644
--- a/tests/orchestrator/context/test_operation.py
+++ b/tests/orchestrator/context/test_operation.py
@@ -404,7 +404,7 @@ def test_attribute_consumption(ctx, executor, dataholder):
     assert source_node.attributes['key'] != target_node.attributes['key']
     assert source_node.attributes['key'].value == \
            target_node.attributes['key'].value == \
-           dataholder['key']
+           dataholder['key'] == 'value'
 
 
 def _assert_loggins(ctx, inputs):
@@ -490,7 +490,6 @@ def _test_plugin_workdir(ctx, filename, content):
 
 @operation
 def attribute_altering_operation(ctx, attributes_dict, **_):
-    import pydevd; pydevd.settrace('localhost', suspend=False)
     ctx.node.attributes.update(attributes_dict)
 
 
@@ -498,7 +497,7 @@ def attribute_altering_operation(ctx, attributes_dict, **_):
 def attribute_consuming_operation(ctx, holder_path, **_):
     holder = helpers.FilesystemDataHolder(holder_path)
     ctx.target_node.attributes.update(ctx.source_node.attributes)
-    holder.update(**ctx.source_node.attributes)
+    holder.update(**ctx.target_node.attributes)
 
 
 class MockActor(object):
@@ -525,42 +524,42 @@ class TestDict(object):
     def model(self):
         return MockModel()
 
-    def test_keys(self, model, actor):
-        dict_ = common._InstrumentedDict(actor.attributes_dict, model)
+    @pytest.fixture
+    def dict_(self, actor, model):
+        return common._InstrumentedDict(
+            actor.attributes_dict, model, actor=actor, 
field_name='attributes_dict')
+
+    def test_keys(self, dict_):
         dict_.update(
             {
                 'key1': Parameter.wrap('key1', 'value1'),
-                'key2': Parameter.wrap('key1', 'value2')
+                'key2': Parameter.wrap('key2', 'value2')
             }
         )
         assert sorted(dict_.keys()) == sorted(['key1', 'key2'])
 
-    def test_values(self, model, actor):
-        dict_ = common._InstrumentedDict(actor.attributes_dict, model)
+    def test_values(self, dict_):
         dict_.update({
             'key1': Parameter.wrap('key1', 'value1'),
             'key2': Parameter.wrap('key1', 'value2')
         })
         assert sorted(dict_.values()) == sorted(['value1', 'value2'])
 
-    def test_items(self, actor, model):
-        dict_ = common._InstrumentedDict(actor.attributes_dict, model)
+    def test_items(self, dict_):
         dict_.update({
             'key1': Parameter.wrap('key1', 'value1'),
             'key2': Parameter.wrap('key1', 'value2')
         })
         assert sorted(dict_.items()) == sorted([('key1', 'value1'), ('key2', 
'value2')])
 
-    def test_iter(self, actor, model):
-        dict_ = common._InstrumentedDict(actor.attributes_dict, model)
+    def test_iter(self, dict_):
         dict_.update({
             'key1': Parameter.wrap('key1', 'value1'),
             'key2': Parameter.wrap('key1', 'value2')
         })
         assert sorted(list(dict_)) == sorted(['key1', 'key2'])
 
-    def test_bool(self, actor, model):
-        dict_ = common._InstrumentedDict(actor.attributes_dict, model)
+    def test_bool(self, dict_):
         assert not dict_
         dict_.update({
             'key1': Parameter.wrap('key1', 'value1'),
@@ -568,8 +567,7 @@ class TestDict(object):
         })
         assert dict_
 
-    def test_set_item(self, actor, model):
-        dict_ = common._InstrumentedDict(actor.attributes_dict, model)
+    def test_set_item(self, dict_):
         dict_['key1'] = Parameter.wrap('key1', 'value1')
         assert dict_['key1'] == 'value1'
         assert isinstance(dict_._parent['key1'], Parameter)
@@ -583,14 +581,12 @@ class TestDict(object):
         assert isinstance(dict_['key1'], common._InstrumentedDict)
         assert dict_['key1']['inner_key'] == 'value2'
 
-    def test_get_item(self, actor, model):
-        dict_ = common._InstrumentedDict(actor.attributes_dict, model)
+    def test_get_item(self, dict_):
         dict_['key1'] = Parameter.wrap('key1', 'value1')
 
         assert isinstance(dict_._parent['key1'], Parameter)
 
-    def test_update(self, actor, model):
-        dict_ = common._InstrumentedDict(actor.attributes_dict, model)
+    def test_update(self, dict_):
         dict_['key1'] = 'value1'
 
         new_dict = {'key2': 'value2'}
@@ -603,8 +599,7 @@ class TestDict(object):
         new_dict.update(dict_)
         assert new_dict['key1'] == dict_['key1']
 
-    def test_copy(self, actor, model):
-        dict_ = common._InstrumentedDict(actor.attributes_dict, model)
+    def test_copy(self, dict_):
         dict_['key1'] = 'value1'
 
         new_dict = dict_.copy()
@@ -615,8 +610,7 @@ class TestDict(object):
         assert new_dict['key1'] == 'value1'
         assert dict_['key1'] == 'value2'
 
-    def test_clear(self, actor, model):
-        dict_ = common._InstrumentedDict(actor.attributes_dict, model)
+    def test_clear(self, dict_):
         dict_['key1'] = 'value1'
         dict_.clear()
 
@@ -632,8 +626,12 @@ class TestList(object):
     def model(self):
         return MockModel()
 
-    def test_insert(self, model, actor):
-        list_ = common._InstrumentedList(actor.attributes_list, model)
+    @pytest.fixture
+    def list_(self, actor, model):
+        return common._InstrumentedList(
+            actor.attributes_list, model, actor=actor, 
field_name='attributes_list')
+
+    def test_insert(self, list_):
         list_.append(Parameter.wrap('name', 'value1'))
         list_.append('value2')
 
@@ -651,8 +649,7 @@ class TestList(object):
         assert list_[0] == 'new_value1'
         assert list_[1] == 'new_value2'
 
-    def test_insert_into_nested(self, model, actor):
-        list_ = common._InstrumentedList(actor.attributes_list, model)
+    def test_insert_into_nested(self, list_):
         list_.append([])
 
         list_[0].append('inner_item')

Reply via email to