added documentation and testing
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/4e851057 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/4e851057 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/4e851057 Branch: refs/heads/ARIA-258-Convert-runtime-properties-to-attributes Commit: 4e8510573d27377d88c9e7811153c2e1c96a9144 Parents: 82d9616 Author: max-orlov <ma...@gigaspaces.com> Authored: Mon May 22 16:46:50 2017 +0300 Committer: max-orlov <ma...@gigaspaces.com> Committed: Mon May 22 16:46:50 2017 +0300 ---------------------------------------------------------------------- aria/orchestrator/context/common.py | 169 ++++++++++++++-------- tests/orchestrator/context/test_operation.py | 48 ++++-- 2 files changed, 147 insertions(+), 70 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4e851057/aria/orchestrator/context/common.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/context/common.py b/aria/orchestrator/context/common.py index 07596f6..a5c01fa 100644 --- a/aria/orchestrator/context/common.py +++ b/aria/orchestrator/context/common.py @@ -203,69 +203,132 @@ class BaseContext(object): class _InstrumentedCollection(object): - def _get_instrumented_collection(self, key, value): + + def __init__(self, + model, + parent, + field_name=None, + item_cls=None, + seq=None, + is_top_level=True, + **kwargs): + self._model = model + self._parent = parent + self._field_name = field_name + self._item_cls = item_cls + self._is_top_level = is_top_level + self._load(seq, **kwargs) + + def _load(self, seq, **kwargs): + """ + Instantiates the object from existing seq. + + :param seq: the original sequence to load from + :return: + """ + raise NotImplementedError + + def _set_field(self, collection, key, value): + """ + enables updating the current change in the ancestors + :param collection: the collection to change + :param key: the key for the specific field + :param value: the new value + :return: + """ + raise NotImplementedError + + def _set(self, key, value): + """ + set the changes for the current object (not in the db) + + :param key: + :param value: + :return: + """ + raise NotImplementedError + + def _instrument(self, key, value): + """ + Instruments any collection to track changes (and ease of access) + :param key: + :param value: + :return: + """ if isinstance(value, _InstrumentedCollection): return value elif isinstance(value, dict): - return _InstrumentedDict(self._model, self, key, seq=value) + instrumentation_cls = _InstrumentedDict elif isinstance(value, list): - return _InstrumentedList(self._model, self, key, seq=value) + instrumentation_cls = _InstrumentedList + else: + return value - return value + return instrumentation_cls(self._model, self, key, self._item_cls, value, False) def _raw_value(self, value): - if self._item_cls and isinstance(value, self._item_cls): + """ + Get the raw value. + :param value: + :return: + """ + if self._is_top_level and isinstance(value, self._item_cls): return value.value return value def _encapsulate_value(self, key, value): + """ + Create a new item cls if needed. + :param key: + :param value: + :return: + """ if isinstance(value, self._item_cls): return value # If it is not wrapped return self._item_cls.wrap(key, value) def __setitem__(self, key, value): + """ + Update the values in both the local and the db locations. + :param key: + :param value: + :return: + """ self._set(key, value) - if self._item_cls: - # We are at the top level + mapi = getattr(self._model, self._item_cls.__modelname__) + if self._is_top_level: field = getattr(self._parent, self._field_name) - mapi = getattr(self._model, self._item_cls.__modelname__) - + # We are at the top level if key in field: # Is this a existing field - value = self._set_parent(field, key, value) + value = self._set_field(field, key, value) else: - value = self._set_parent(field, key, self._encapsulate_value(key, value)) - - mapi.update(value) + value = self._set_field(field, key, self._encapsulate_value(key, value)) else: - self._set(key, value) # We are not at the top level - self._parent[self._field_name] = self + value = self._set_field(self._parent, self._field_name, self) + + mapi.update(value) class _InstrumentedDict(_InstrumentedCollection, dict): - def __init__(self, model, parent, field_name=None, item_cls=None, seq=None, **kwargs): - self._model = model - self._parent = parent - self._field_name = field_name - self._item_cls = item_cls - self._load(seq, **kwargs) - def _load(self, seq=None, **kwargs): - seq = dict((key, value.value if isinstance(value, self._item_cls) else value) - for key, value in (seq or {}).items()) - super(_InstrumentedDict, self).__init__(seq, **kwargs) + def _load(self, dict_=None, **kwargs): + dict.__init__( + self, + tuple((key, self._raw_value(value)) for key, value in (dict_ or {}).items()), + **kwargs) - def update(self, E=None, **F): - E = E or {} - for key, value in E.items(): + def update(self, dict_=None, **kwargs): + dict_ = dict_ or {} + for key, value in dict_.items(): self[key] = value - for key, value in F.items(): + for key, value in kwargs.items(): self[key] = value def __getitem__(self, key): - return self._get_instrumented_collection(key, dict.__getitem__(self, key)) + return self._instrument(key, dict.__getitem__(self, key)) def values(self): return [self[key] for key in self.keys()] @@ -279,53 +342,45 @@ class _InstrumentedDict(_InstrumentedCollection, dict): def _set(self, key, value): dict.__setitem__(self, key, self._raw_value(value)) - def _set_parent(self, field, key, value): - if key in field and isinstance(field[key], self._item_cls): - if isinstance(field[key], dict): - field[key].clear() - field[key].value = value + def _set_field(self, collection, key, value): + if key in collection and isinstance(collection[key], self._item_cls): + if isinstance(collection[key], dict): + collection[key].clear() + collection[key].value = value else: - field[key] = value - return field[key] - + collection[key] = value + return collection[key] -class _InstrumentedList(list, _InstrumentedCollection): - def __init__(self, model, parent, field_name=None, item_cls=None, seq=None, **kwargs): - self._model = model - self._parent = parent - self._field_name = field_name - self._item_cls = item_cls - self._load(seq, **kwargs) - def _load(self, seq=None, **kwargs): - seq = list(item for item in seq or []) - super(_InstrumentedList, self).__init__(seq) +class _InstrumentedList(_InstrumentedCollection, list): + def _load(self, list_=None, **kwargs): + list.__init__(self, list(item for item in list_ or [])) def append(self, value): self.insert(len(self), value) def insert(self, index, value): list.insert(self, index, self._raw_value(value)) - if self._item_cls: + if self._is_top_level: field = getattr(self._parent, self._field_name) field.insert(index, self._encapsulate_value(index, value)) else: self._parent[self._field_name] = self def __getitem__(self, key): - return self._get_instrumented_collection(key, list.__getitem__(self, key)) + return self._instrument(key, list.__getitem__(self, key)) def _set(self, key, value): list.__setitem__(self, key, value) - def _set_parent(self, field, key, value): - if key in field and isinstance(field[key], self._item_cls): - if isinstance(field[key], list): - del field[key] - field[key].value = value + def _set_field(self, collection, key, value): + if key in collection and isinstance(collection[key], self._item_cls): + if isinstance(collection[key], list): + del collection[key] + collection[key].value = value else: - field[key] = value - return field[key] + collection[key] = value + return collection[key] class InstrumentCollection(object): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4e851057/tests/orchestrator/context/test_operation.py ---------------------------------------------------------------------- diff --git a/tests/orchestrator/context/test_operation.py b/tests/orchestrator/context/test_operation.py index 73efff5..86e4a24 100644 --- a/tests/orchestrator/context/test_operation.py +++ b/tests/orchestrator/context/test_operation.py @@ -514,7 +514,7 @@ class MockModel(object): 'update': lambda *args, **kwargs: None})() -class TestDict(object): +class ContextSugaring(object): @pytest.fixture def actor(self): @@ -528,6 +528,13 @@ class TestDict(object): def dict_(self, actor, model): return common._InstrumentedDict(model, actor, 'dict_', item_cls=Parameter) + @pytest.fixture + def list_(self, actor, model): + return common._InstrumentedList(model, actor, field_name='list_', item_cls=Parameter) + + +class TestDict(ContextSugaring): + def test_keys(self, actor, dict_): dict_.update( { @@ -643,18 +650,7 @@ class TestDict(object): assert len(dict_) == 0 -class TestList(object): - @pytest.fixture - def actor(self): - return MockActor() - - @pytest.fixture - def model(self): - return MockModel() - - @pytest.fixture - def list_(self, actor, model): - return common._InstrumentedList(model, actor, field_name='list_', item_cls=Parameter) +class TestList(ContextSugaring): def test_append(self, actor, list_): list_.append(Parameter.wrap('name', 'value1')) @@ -710,3 +706,29 @@ class TestList(object): assert list_[0] == ['inner_item', 'new_item'] assert ['inner_item', 'new_item'] == list_[0] + + +class TestDictList(ContextSugaring): + def test_dict_in_list(self, actor, list_): + list_.append({}) + assert len(list_) == 1 + assert isinstance(actor.list_[0], Parameter) + assert actor.list_[0].value == {} + + list_[0]['key'] = 'value' + assert list_[0]['key'] == 'value' + assert len(actor.list_) == 1 + assert isinstance(actor.list_[0], Parameter) + assert actor.list_[0].value['key'] == 'value' + + def test_list_in_dict(self, actor, dict_): + dict_['key'] = [] + assert len(dict_) == 1 + assert isinstance(actor.dict_['key'], Parameter) + assert actor.dict_['key'].value == [] + + dict_['key'].append('value') + assert dict_['key'][0] == 'value' + assert len(actor.dict_) == 1 + assert isinstance(actor.dict_['key'], Parameter) + assert actor.dict_['key'].value[0] == 'value'