Repository: incubator-ariatosca Updated Branches: refs/heads/ARIA-258-Convert-runtime-properties-to-attributes 9786090d0 -> 5e5a25389
created a list instrumentation and added some tests Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/5e5a2538 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/5e5a2538 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/5e5a2538 Branch: refs/heads/ARIA-258-Convert-runtime-properties-to-attributes Commit: 5e5a25389f41de9c3a37aaf7e68ff12a987c34a5 Parents: 9786090 Author: max-orlov <[email protected]> Authored: Wed May 17 18:27:28 2017 +0300 Committer: max-orlov <[email protected]> Committed: Wed May 17 18:27:28 2017 +0300 ---------------------------------------------------------------------- aria/orchestrator/context/common.py | 144 ++++++++++++++-------- aria/orchestrator/context/operation.py | 12 +- tests/orchestrator/context/test_operation.py | 79 +++++++++--- 3 files changed, 159 insertions(+), 76 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/5e5a2538/aria/orchestrator/context/common.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/context/common.py b/aria/orchestrator/context/common.py index 83f7215..d41566b 100644 --- a/aria/orchestrator/context/common.py +++ b/aria/orchestrator/context/common.py @@ -201,57 +201,62 @@ class BaseContext(object): self.model.log._session.close() self.model.log._engine.dispose() -class _Dict(collections.MutableMapping): - def __init__(self, actor, model, nested=None): - super(_Dict, self).__init__() + +class _InstrumentedCollection(object): + def __init__(self, actor, attributes, model, nested=None): + super(_InstrumentedCollection, self).__init__() self._actor = actor - self._attributes = self._actor.attributes + self._attributes = attributes self._model = model self._attr_cls = self._model.parameter.model_cls self._nested = nested or [] + @property + def _nested_value(self): + current = self._attributes + for k in self._nested: + current = current[k] + return current.value if isinstance(current, self._attr_cls) else current + + def __getitem__(self, index): + value = self._nested_value[index] if self._nested else self._attributes[index].value + + if isinstance(value, list): + return _List(self._actor, + self._attributes, + self._model, + nested=self._nested + [index]) + elif isinstance(value, dict): + return _Dict(self._actor, + self._attributes, + self._model, + nested=self._nested + [index]) + elif isinstance(value, self._attr_cls): + return value.value + + return value + def __delitem__(self, key): del self._nested_value[key] - def __contains__(self, item): - for key in self.keys(): - if item == key: - return True - return False - def __len__(self): return len(self._nested_value) def __nonzero__(self): return bool(self._nested_value) - def __getitem__(self, item): - if self._nested: - value = self._nested_value[item] - else: - value = self._attributes[item].value - if isinstance(value, dict): - return _Dict(self._actor, self._model, nested=self._nested + [item]) - elif isinstance(value, self._attr_cls): - return value.value - return value + +class _Dict(_InstrumentedCollection, collections.MutableMapping): def __setitem__(self, key, value): if self._nested or key in self._attributes: attribute = self._update_attr(key, value) self._model.parameter.update(attribute) else: - attr = self._attr_cls.wrap(key, value) + attr = value if isinstance(value, self._attr_cls) else self._attr_cls.wrap(key, value) self._attributes[key] = attr self._model.parameter.put(attr) - @property - def _nested_value(self): - current = self._attributes - for k in self._nested: - current = current[k] - return current.value if isinstance(current, self._attr_cls) else current - def _update_attr(self, key, value): current = self._attributes @@ -272,15 +277,12 @@ class _Dict(collections.MutableMapping): # Since this a user defined parameter, this doesn't track changes. So we override the entire # thing. - if isinstance(attribute.value, dict): + if isinstance(attribute.value, (dict, list)): value = attribute.value.copy() attribute.value.clear() attribute.value = value return attribute - def _unwrap(self, attr): - return attr.unwrap() if isinstance(attr, self._attr_cls) else attr - def keys(self): dict_ = (self._nested_value.value if isinstance(self._nested_value, self._attr_cls) @@ -322,26 +324,55 @@ class _Dict(collections.MutableMapping): def clear(self): self._nested_value.clear() - def update(self, dict_=None, **kwargs): - if dict_: - for key, value in dict_.items(): - self[key] = value - for key, value in kwargs.items(): - self[key] = value +class _List(_InstrumentedCollection, collections.MutableSequence): + def insert(self, index, value): + if self._nested: + attribute = self._update_attr(index, value) + self._model.parameter.update(attribute) + elif len(self._attributes) > index: + self._attributes[index].value = value + self._model.parameter.update(self._attributes[index]) + else: + attr = value if isinstance(value, self._attr_cls) else self._attr_cls.wrap(index, value) + self._attributes.insert(index, attr) + self._model.parameter.put(self._attributes) + + def __setitem__(self, index, value): + return self.insert(index, value) + + def _update_attr(self, index, value): + attribute = current = self._attributes[self._nested[0]] + for i in self._nested[1:]: + current = current[i] + if isinstance(current, self._attr_cls): + if isinstance(current.value, list): + current.value.insert(index, value) + else: + current.value[index] = value + else: + current[index] = value -class DecorateAttributes(dict): + if isinstance(attribute.value, dict): + value = attribute.value.copy() + attribute.value.clear() + attribute.value = value + elif isinstance(attribute.value, list): + attribute.value[:] = current.value - def __init__(self, func): - super(DecorateAttributes, self).__init__() - self._func = func - self._attributes = None - self._actor = None + return attribute - @property - def attributes(self): - return self._attributes + def __eq__(self, other): + return self._nested_value.__eq__(other) + + +class InstrumentCollection(object): + + def __init__(self, field_name): + super(InstrumentCollection, self).__init__() + self._field_name = field_name + self._actor = None @property def actor(self): @@ -351,10 +382,15 @@ class DecorateAttributes(dict): try: return getattr(self._actor, item) except AttributeError: - return super(DecorateAttributes, self).__getattribute__(item) - - def __call__(self, *args, **kwargs): - func_self = args[0] - self._actor = self._func(*args, **kwargs) - self._attributes = _Dict(self._actor, func_self.model) - return self + return super(InstrumentCollection, self).__getattribute__(item) + + def __call__(self, func, *args, **kwargs): + 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, _Dict(self._actor, field, func_self.model)) + elif isinstance(field, list): + setattr(self, self._field_name, _List(self._actor, field, func_self.model)) + return self + return _wrapper http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/5e5a2538/aria/orchestrator/context/operation.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/context/operation.py b/aria/orchestrator/context/operation.py index f4e8813..fef3ecf 100644 --- a/aria/orchestrator/context/operation.py +++ b/aria/orchestrator/context/operation.py @@ -105,7 +105,7 @@ class NodeOperationContext(BaseOperationContext): """ @property - @common.DecorateAttributes + @common.InstrumentCollection('attributes') def node_template(self): """ the node of the current operation @@ -114,7 +114,7 @@ class NodeOperationContext(BaseOperationContext): return self.node.node_template @property - @common.DecorateAttributes + @common.InstrumentCollection('attributes') def node(self): """ The node instance of the current operation @@ -129,7 +129,7 @@ class RelationshipOperationContext(BaseOperationContext): """ @property - @common.DecorateAttributes + @common.InstrumentCollection('attributes') def source_node_template(self): """ The source node @@ -138,7 +138,7 @@ class RelationshipOperationContext(BaseOperationContext): return self.source_node.node_template @property - @common.DecorateAttributes + @common.InstrumentCollection('attributes') def source_node(self): """ The source node instance @@ -147,7 +147,7 @@ class RelationshipOperationContext(BaseOperationContext): return self.relationship.source_node @property - @common.DecorateAttributes + @common.InstrumentCollection('attributes') def target_node_template(self): """ The target node @@ -156,7 +156,7 @@ class RelationshipOperationContext(BaseOperationContext): return self.target_node.node_template @property - @common.DecorateAttributes + @common.InstrumentCollection('attributes') def target_node(self): """ The target node instance http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/5e5a2538/tests/orchestrator/context/test_operation.py ---------------------------------------------------------------------- diff --git a/tests/orchestrator/context/test_operation.py b/tests/orchestrator/context/test_operation.py index 5ce0b22..44823d1 100644 --- a/tests/orchestrator/context/test_operation.py +++ b/tests/orchestrator/context/test_operation.py @@ -502,7 +502,8 @@ def attribute_consuming_operation(ctx, holder_path, **_): class MockActor(object): def __init__(self): - self.attributes = {} + self.attributes_dict = {} + self.attributes_list = [] class MockModel(object): @@ -524,8 +525,8 @@ class TestDict(object): return MockModel() def test_keys(self, model, actor): - dict_ = common._Dict(actor, model) - actor.attributes.update( + dict_ = common._Dict(actor, actor.attributes_dict, model) + actor.attributes_dict.update( { 'key1': Parameter.wrap('key1', 'value1'), 'key2': Parameter.wrap('key1', 'value2') @@ -534,40 +535,40 @@ class TestDict(object): assert sorted(dict_.keys()) == sorted(['key1', 'key2']) def test_values(self, model, actor): - dict_ = common._Dict(actor, model) - actor.attributes.update({ + dict_ = common._Dict(actor, actor.attributes_dict, model) + actor.attributes_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._Dict(actor, model) - actor.attributes.update({ + dict_ = common._Dict(actor, actor.attributes_dict, model) + actor.attributes_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._Dict(actor, model) - actor.attributes.update({ + dict_ = common._Dict(actor, actor.attributes_dict, model) + actor.attributes_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._Dict(actor, model) + dict_ = common._Dict(actor, actor.attributes_dict, model) assert not dict_ - actor.attributes.update({ + actor.attributes_dict.update({ 'key1': Parameter.wrap('key1', 'value1'), 'key2': Parameter.wrap('key1', 'value2') }) assert dict_ def test_set_item(self, actor, model): - dict_ = common._Dict(actor, model) + dict_ = common._Dict(actor, actor.attributes_dict, model) dict_['key1'] = Parameter.wrap('key1', 'value1') assert 'key1' in dict_ assert isinstance(dict_._attributes['key1'], Parameter) @@ -583,13 +584,13 @@ class TestDict(object): assert dict_['key1']['inner_key'] == 'value2' def test_get_item(self, actor, model): - dict_ = common._Dict(actor, model) + dict_ = common._Dict(actor, actor.attributes_dict, model) dict_['key1'] = Parameter.wrap('key1', 'value1') assert isinstance(dict_._attributes['key1'], Parameter) def test_update(self, actor, model): - dict_ = common._Dict(actor, model) + dict_ = common._Dict(actor, actor.attributes_dict, model) dict_['key1'] = 'value1' new_dict = {'key2': 'value2'} @@ -603,7 +604,7 @@ class TestDict(object): assert new_dict['key1'] == dict_['key1'] def test_copy(self, actor, model): - dict_ = common._Dict(actor, model) + dict_ = common._Dict(actor, actor.attributes_dict, model) dict_['key1'] = 'value1' new_dict = dict_.copy() @@ -615,8 +616,54 @@ class TestDict(object): assert dict_['key1'] == 'value2' def test_clear(self, actor, model): - dict_ = common._Dict(actor, model) + dict_ = common._Dict(actor, actor.attributes_dict, model) dict_['key1'] = 'value1' dict_.clear() assert len(dict_) == 0 + + +class TestList(object): + @pytest.fixture + def actor(self): + return MockActor() + + @pytest.fixture + def model(self): + return MockModel() + + def test_insert(self, model, actor): + list_ = common._List(actor, actor.attributes_list, model) + list_.append(Parameter.wrap('name', 'value1')) + list_.append('value2') + + assert len(list_) == 2 + assert isinstance(list_._attributes[0], Parameter) + assert list_[0] == 'value1' + + assert isinstance(list_._attributes[1], Parameter) + assert list_[1] == 'value2' + + list_[0] = 'new_value1' + list_[1] = 'new_value2' + assert isinstance(list_._attributes[1], Parameter) + assert isinstance(list_._attributes[1], Parameter) + assert list_[0] == 'new_value1' + assert list_[1] == 'new_value2' + + def test_insert_into_nested(self, model, actor): + list_ = common._List(actor, actor.attributes_list, model) + list_.append([]) + + list_[0].append('inner_item') + assert isinstance(list_._attributes[0], Parameter) + assert len(list_) == 1 + assert list_[0][0] == 'inner_item' + + list_[0].append('new_item') + assert isinstance(list_._attributes[0], Parameter) + assert len(list_) == 1 + assert list_[0][1] == 'new_item' + + assert list_[0] == ['inner_item', 'new_item'] + assert ['inner_item', 'new_item'] == list_[0]
