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')
