You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ariatosca.apache.org by mx...@apache.org on 2017/05/16 18:38:24 UTC

incubator-ariatosca git commit: removed instumentation and fixed linting

Repository: incubator-ariatosca
Updated Branches:
  refs/heads/runtime_props_to_attr b47ad4295 -> 18d218542


removed instumentation and fixed linting


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

Branch: refs/heads/runtime_props_to_attr
Commit: 18d2185422f38e395659fdb5b566ed215c756d8c
Parents: b47ad42
Author: max-orlov <ma...@gigaspaces.com>
Authored: Tue May 16 21:38:19 2017 +0300
Committer: max-orlov <ma...@gigaspaces.com>
Committed: Tue May 16 21:38:19 2017 +0300

----------------------------------------------------------------------
 aria/modeling/types.py                          |  20 -
 aria/orchestrator/context/common.py             |  13 +-
 aria/orchestrator/workflows/executor/process.py |   1 -
 aria/storage/instrumentation.py                 | 321 ---------------
 tests/orchestrator/context/test_operation.py    |  28 +-
 tests/storage/test_instrumentation.py           | 396 -------------------
 6 files changed, 26 insertions(+), 753 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/18d21854/aria/modeling/types.py
----------------------------------------------------------------------
diff --git a/aria/modeling/types.py b/aria/modeling/types.py
index 7460f47..920a0c2 100644
--- a/aria/modeling/types.py
+++ b/aria/modeling/types.py
@@ -286,24 +286,4 @@ _LISTENER_ARGS = (mutable.mapper, 'mapper_configured', _mutable_association_list
 def _register_mutable_association_listener():
     event.listen(*_LISTENER_ARGS)
 
-
-def remove_mutable_association_listener():
-    """
-    Remove the event listener that associates ``Dict`` and ``List`` column types with
-    ``MutableDict`` and ``MutableList``, respectively.
-
-    This call must happen before any model instance is instantiated.
-    This is because once it does, that would trigger the listener we are trying to remove.
-    Once it is triggered, many other listeners will then be registered.
-    At that point, it is too late.
-
-    The reason this function exists is that the association listener, interferes with ARIA change
-    tracking instrumentation, so a way to disable it is required.
-
-    Note that the event listener this call removes is registered by default.
-    """
-    if event.contains(*_LISTENER_ARGS):
-        event.remove(*_LISTENER_ARGS)
-
-
 _register_mutable_association_listener()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/18d21854/aria/orchestrator/context/common.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/common.py b/aria/orchestrator/context/common.py
index 260ccea..9758bb5 100644
--- a/aria/orchestrator/context/common.py
+++ b/aria/orchestrator/context/common.py
@@ -333,6 +333,16 @@ class DecorateAttributes(dict):
     def __init__(self, func):
         super(DecorateAttributes, self).__init__()
         self._func = func
+        self._attributes = None
+        self._actor = None
+
+    @property
+    def attributes(self):
+        return self._attributes
+
+    @property
+    def actor(self):
+        return self._actor
 
     def __getattr__(self, item):
         try:
@@ -343,6 +353,5 @@ class DecorateAttributes(dict):
     def __call__(self, *args, **kwargs):
         func_self = args[0]
         self._actor = self._func(*args, **kwargs)
-        self._model = func_self.model
-        self.attributes = _Dict(self._actor, self._model)
+        self._attributes = _Dict(self._actor, func_self.model)
         return self

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/18d21854/aria/orchestrator/workflows/executor/process.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/executor/process.py b/aria/orchestrator/workflows/executor/process.py
index f3a08ec..d15d878 100644
--- a/aria/orchestrator/workflows/executor/process.py
+++ b/aria/orchestrator/workflows/executor/process.py
@@ -49,7 +49,6 @@ from aria.utils import (
     exceptions,
     process as process_utils
 )
-from aria.modeling import types as modeling_types
 
 
 _INT_FMT = 'I'

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/18d21854/aria/storage/instrumentation.py
----------------------------------------------------------------------
diff --git a/aria/storage/instrumentation.py b/aria/storage/instrumentation.py
deleted file mode 100644
index eb5ff6c..0000000
--- a/aria/storage/instrumentation.py
+++ /dev/null
@@ -1,321 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import copy
-import json
-import os
-
-import sqlalchemy.event
-
-from ..modeling import models as _models
-from ..storage.exceptions import StorageError
-
-
-_VERSION_ID_COL = 'version'
-_STUB = object()
-_Collection = type('_Collection', (object, ), {})
-
-collection = _Collection()
-_INSTRUMENTED = {
-    'modified': {
-        _models.Node.state: str,
-        _models.Task.status: str,
-        _models.Node.attributes: collection,
-        # TODO: add support for pickled type
-        _models.Parameter._value: lambda x: x
-    },
-    'new': (_models.Log, ),
-
-}
-
-_NEW_INSTANCE = 'NEW_INSTANCE'
-
-
-def track_changes(model=None, instrumented=None):
-    """Track changes in the specified model columns
-
-    This call will register event listeners using sqlalchemy's event mechanism. The listeners
-    instrument all returned objects such that the attributes specified in ``instrumented``, will
-    be replaced with a value that is stored in the returned instrumentation context
-    ``tracked_changes`` property.
-
-    Why should this be implemented when sqlalchemy already does a fantastic job at tracking changes
-    you ask? Well, when sqlalchemy is used with sqlite, due to how sqlite works, only one process
-    can hold a write lock to the database. This does not work well when ARIA runs tasks in
-    subprocesses (by the process executor) and these tasks wish to change some state as well. These
-    tasks certainly deserve a chance to do so!
-
-    To enable this, the subprocess calls ``track_changes()`` before any state changes are made.
-    At the end of the subprocess execution, it should return the ``tracked_changes`` attribute of
-    the instrumentation context returned from this call, to the parent process. The parent process
-    will then call ``apply_tracked_changes()`` that resides in this module as well.
-    At that point, the changes will actually be written back to the database.
-
-    :param model: the model storage. it should hold a mapi for each model. the session of each mapi
-    is needed to setup events
-    :param instrumented: A dict from model columns to their python native type
-    :return: The instrumentation context
-    """
-    return _Instrumentation(model, instrumented or _INSTRUMENTED)
-
-
-class _Instrumentation(object):
-
-    def __init__(self, model, instrumented):
-        self.tracked_changes = {}
-        self.new_instances_as_dict = {}
-        self.listeners = []
-        self._instances_to_expunge = []
-        self._model = model
-        self._track_changes(instrumented)
-
-    @property
-    def _new_instance_id(self):
-        return '{prefix}_{index}'.format(prefix=_NEW_INSTANCE,
-                                         index=len(self._instances_to_expunge))
-
-    def expunge_session(self):
-        for new_instance in self._instances_to_expunge:
-            self._get_session_from_model(new_instance.__tablename__).expunge(new_instance)
-
-    def _get_session_from_model(self, tablename):
-        mapi = getattr(self._model, tablename, None)
-        if mapi:
-            return mapi._session
-        raise StorageError("Could not retrieve session for {0}".format(tablename))
-
-    def _track_changes(self, instrumented):
-        instrumented_attribute_classes = {}
-        # Track any newly created instances.
-        for instrumented_class in instrumented.get('new', []):
-            self._register_new_instance_listener(instrumented_class)
-
-        # Track any newly-set attributes.
-        for instrumented_attribute, attribute_type in instrumented.get('modified', {}).items():
-            self._register_attribute_listener(instrumented_attribute=instrumented_attribute,
-                                              attribute_type=attribute_type)
-            if not isinstance(attribute_type, _Collection):
-                instrumented_class = instrumented_attribute.parent.entity
-                instrumented_class_attributes = instrumented_attribute_classes.setdefault(
-                    instrumented_class, {})
-                instrumented_class_attributes[instrumented_attribute.key] = attribute_type
-
-        # Track any global instance update such as 'refresh' or 'load'
-        for instrumented_class, instrumented_attributes in instrumented_attribute_classes.items():
-            self._register_instance_listeners(instrumented_class=instrumented_class,
-                                              instrumented_attributes=instrumented_attributes)
-
-    def _register_new_instance_listener(self, instrumented_class):
-        if self._model is None:
-            raise StorageError("In order to keep track of new instances, a ctx is needed")
-
-        def listener(_, instance):
-            if not isinstance(instance, instrumented_class):
-                return
-            self._instances_to_expunge.append(instance)
-            tracked_instances = self.new_instances_as_dict.setdefault(instance.__modelname__, {})
-            tracked_attributes = tracked_instances.setdefault(self._new_instance_id, {})
-            instance_as_dict = instance.to_dict()
-            instance_as_dict.update((k, getattr(instance, k))
-                                    for k in getattr(instance, '__private_fields__', []))
-            tracked_attributes.update(instance_as_dict)
-        session = self._get_session_from_model(instrumented_class.__tablename__)
-        listener_args = (session, 'after_attach', listener)
-        sqlalchemy.event.listen(*listener_args)
-        self.listeners.append(listener_args)
-
-    def _register_attribute_listener(self, instrumented_attribute, attribute_type):
-        # Track and newly created instances that are a part of a collection.
-        if isinstance(attribute_type, _Collection):
-            return self._register_append_to_attribute_listener(instrumented_attribute)
-        else:
-            return self._register_set_attribute_listener(instrumented_attribute, attribute_type)
-
-    def _register_append_to_attribute_listener(self, collection_attr):
-        def listener(target, value, initiator):
-            import pydevd; pydevd.settrace('localhost', suspend=False)
-            tracked_instances = self.tracked_changes.setdefault(target.__modelname__, {})
-            tracked_attributes = tracked_instances.setdefault(target.id, {})
-            collection_attr = tracked_attributes.setdefault(initiator.key, [])
-            instance_as_dict = value.to_dict()
-            instance_as_dict.update((k, getattr(value, k))
-                                    for k in getattr(value, '__private_fields__', []))
-            instance_as_dict['_MODEL_CLS'] = value.__modelname__
-            collection_attr.append(instance_as_dict)
-
-        listener_args = (collection_attr, 'append', listener)
-        sqlalchemy.event.listen(*listener_args)
-        self.listeners.append(listener_args)
-
-    def _register_set_attribute_listener(self, instrumented_attribute, attribute_type):
-        def listener(target, value, *_):
-            import pydevd; pydevd.settrace('localhost', suspend=False)
-            mapi_name = target.__modelname__
-            tracked_instances = self.tracked_changes.setdefault(mapi_name, {})
-            tracked_attributes = tracked_instances.setdefault(target.id, {})
-            current = copy.deepcopy(attribute_type(value)) if value else None
-            tracked_attributes[instrumented_attribute.key] = _Value(_STUB, current)
-            return current
-        listener_args = (instrumented_attribute, 'set', listener)
-        sqlalchemy.event.listen(*listener_args, retval=True)
-        self.listeners.append(listener_args)
-
-    def _register_instance_listeners(self, instrumented_class, instrumented_attributes):
-        def listener(target, *_):
-            mapi_name = instrumented_class.__modelname__
-            tracked_instances = self.tracked_changes.setdefault(mapi_name, {})
-            tracked_attributes = tracked_instances.setdefault(target.id, {})
-            if hasattr(target, _VERSION_ID_COL):
-                # We want to keep track of the initial version id so it can be compared
-                # with the committed version id when the tracked changes are applied
-                tracked_attributes.setdefault(_VERSION_ID_COL,
-                                              _Value(_STUB, getattr(target, _VERSION_ID_COL)))
-            for attribute_name, attribute_type in instrumented_attributes.items():
-                if attribute_name not in tracked_attributes:
-                    initial = getattr(target, attribute_name)
-                    if initial is None:
-                        current = None
-                    else:
-                        current = copy.deepcopy(attribute_type(initial))
-                    tracked_attributes[attribute_name] = _Value(initial, current)
-                target.__dict__[attribute_name] = tracked_attributes[attribute_name].current
-        for listener_args in ((instrumented_class, 'load', listener),
-                              (instrumented_class, 'refresh', listener),
-                              (instrumented_class, 'refresh_flush', listener)):
-            sqlalchemy.event.listen(*listener_args)
-            self.listeners.append(listener_args)
-
-    def clear(self, target=None):
-        if target:
-            mapi_name = target.__modelname__
-            tracked_instances = self.tracked_changes.setdefault(mapi_name, {})
-            tracked_instances.pop(target.id, None)
-        else:
-            self.tracked_changes.clear()
-
-        self.new_instances_as_dict.clear()
-        self._instances_to_expunge = []
-
-    def restore(self):
-        """Remove all listeners registered by this instrumentation"""
-        for listener_args in self.listeners:
-            if sqlalchemy.event.contains(*listener_args):
-                sqlalchemy.event.remove(*listener_args)
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        self.restore()
-
-
-class _Value(object):
-    # You may wonder why is this a full blown class and not a named tuple. The reason is that
-    # jsonpickle that is used to serialize the tracked_changes, does not handle named tuples very
-    # well. At the very least, I could not get it to behave.
-
-    def __init__(self, initial, current):
-        self.initial = initial
-        self.current = current
-
-    def __eq__(self, other):
-        if not isinstance(other, _Value):
-            return False
-        return self.initial == other.initial and self.current == other.current
-
-    def __hash__(self):
-        return hash((self.initial, self.current))
-
-    @property
-    def dict(self):
-        return {'initial': self.initial, 'current': self.current}.copy()
-
-
-def apply_tracked_changes(tracked_changes, new_instances, model):
-    """Write tracked changes back to the database using provided model storage
-
-    :param tracked_changes: The ``tracked_changes`` attribute of the instrumentation context
-                            returned by calling ``track_changes()``
-    :param model: The model storage used to actually apply the changes
-    """
-    successfully_updated_changes = dict()
-    try:
-
-        # Handle new instances
-        for mapi_name, new_instance in new_instances.items():
-            successfully_updated_changes[mapi_name] = dict()
-            mapi = getattr(model, mapi_name)
-            for tmp_id, new_instance_kwargs in new_instance.items():
-                instance = mapi.model_cls(**new_instance_kwargs)
-                mapi.put(instance)
-                successfully_updated_changes[mapi_name][instance.id] = new_instance_kwargs
-                new_instance[tmp_id] = instance
-
-        # handle instance updates
-        for mapi_name, tracked_instances in tracked_changes.items():
-            successfully_updated_changes[mapi_name] = dict()
-            mapi = getattr(model, mapi_name)
-
-            for instance_id, tracked_attributes in tracked_instances.items():
-                successfully_updated_changes[mapi_name][instance_id] = dict()
-                instance = None
-                for attribute_name, value in tracked_attributes.items():
-                    instance = instance or mapi.get(instance_id)
-                    if isinstance(value, list):
-                        # The changes are new item to a collection
-                        for item in value:
-                            model_name = item.pop('_MODEL_CLS')
-                            attr_model = getattr(model, model_name).model_cls
-                            new_attr = attr_model(**item)
-                            getattr(instance, attribute_name)[new_attr] = new_attr
-                    elif value.initial != value.current:
-                        # scalar attribute
-                        setattr(instance, attribute_name, value.current)
-                if instance:
-                    _validate_version_id(instance, mapi)
-                    mapi.update(instance)
-                    # TODO: reinstate this
-                    # successfully_updated_changes[mapi_name][instance_id] = [
-                    #     v.dict for v in tracked_attributes.values()]
-
-    except BaseException:
-        for key, value in successfully_updated_changes.items():
-            if not value:
-                del successfully_updated_changes[key]
-        # TODO: if the successful has _STUB, the logging fails because it can't serialize the object
-        model.logger.error(
-            'Registering all the changes to the storage has failed. {0}'
-            'The successful updates were: {0} '
-            '{1}'.format(os.linesep, json.dumps(successfully_updated_changes, indent=4)))
-
-        raise
-
-
-def _validate_version_id(instance, mapi):
-    version_id = sqlalchemy.inspect(instance).committed_state.get(_VERSION_ID_COL)
-    # There are two version conflict code paths:
-    # 1. The instance committed state loaded already holds a newer version,
-    #    in this case, we manually raise the error
-    # 2. The UPDATE statement is executed with version validation and sqlalchemy
-    #    will raise a StateDataError if there is a version mismatch.
-    if version_id and getattr(instance, _VERSION_ID_COL) != version_id:
-        object_version_id = getattr(instance, _VERSION_ID_COL)
-        mapi._session.rollback()
-        raise StorageError(
-            'Version conflict: committed and object {0} differ '
-            '[committed {0}={1}, object {0}={2}]'
-            .format(_VERSION_ID_COL,
-                    version_id,
-                    object_version_id))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/18d21854/tests/orchestrator/context/test_operation.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_operation.py b/tests/orchestrator/context/test_operation.py
index ca5154c..5ce0b22 100644
--- a/tests/orchestrator/context/test_operation.py
+++ b/tests/orchestrator/context/test_operation.py
@@ -513,7 +513,7 @@ class MockModel(object):
                                                         'update': lambda *args, **kwargs: None})()
 
 
-class TestDict():
+class TestDict(object):
 
     @pytest.fixture
     def actor(self):
@@ -525,9 +525,11 @@ class TestDict():
 
     def test_keys(self, model, actor):
         dict_ = common._Dict(actor, model)
-        actor.attributes.update({
-            'key1': Parameter.wrap('key1', 'value1'),
-            'key2': Parameter.wrap('key1', 'value2')}
+        actor.attributes.update(
+            {
+                'key1': Parameter.wrap('key1', 'value1'),
+                'key2': Parameter.wrap('key1', 'value2')
+            }
         )
         assert sorted(dict_.keys()) == sorted(['key1', 'key2'])
 
@@ -535,24 +537,24 @@ class TestDict():
         dict_ = common._Dict(actor, model)
         actor.attributes.update({
             'key1': Parameter.wrap('key1', 'value1'),
-            'key2': Parameter.wrap('key1', 'value2')}
-        )
+            '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({
             'key1': Parameter.wrap('key1', 'value1'),
-            'key2': Parameter.wrap('key1', 'value2')}
-        )
+            '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({
             'key1': Parameter.wrap('key1', 'value1'),
-            'key2': Parameter.wrap('key1', 'value2')}
-        )
+            'key2': Parameter.wrap('key1', 'value2')
+        })
         assert sorted(list(dict_)) == sorted(['key1', 'key2'])
 
     def test_bool(self, actor, model):
@@ -560,8 +562,8 @@ class TestDict():
         assert not dict_
         actor.attributes.update({
             'key1': Parameter.wrap('key1', 'value1'),
-            'key2': Parameter.wrap('key1', 'value2')}
-        )
+            'key2': Parameter.wrap('key1', 'value2')
+        })
         assert dict_
 
     def test_set_item(self, actor, model):
@@ -617,4 +619,4 @@ class TestDict():
         dict_['key1'] = 'value1'
         dict_.clear()
 
-        assert len(dict_) == 0
\ No newline at end of file
+        assert len(dict_) == 0

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/18d21854/tests/storage/test_instrumentation.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_instrumentation.py b/tests/storage/test_instrumentation.py
deleted file mode 100644
index bdbb17e..0000000
--- a/tests/storage/test_instrumentation.py
+++ /dev/null
@@ -1,396 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import pytest
-from sqlalchemy import Column, Text, Integer, event
-
-from aria.modeling import (
-    mixins,
-    types as modeling_types,
-    models
-)
-from aria.modeling.exceptions import ValueFormatException
-from aria.storage import (
-    ModelStorage,
-    sql_mapi,
-    instrumentation
-)
-
-from . import release_sqlite_storage, init_inmemory_model_storage
-
-STUB = instrumentation._STUB
-Value = instrumentation._Value
-instruments_holder = []
-
-
-class TestInstrumentation(object):
-
-    def test_track_changes(self, storage):
-        model_kwargs = dict(
-            name='name',
-            dict1={'initial': 'value'},
-            dict2={'initial': 'value'},
-            list1=['initial'],
-            list2=['initial'],
-            int1=0,
-            int2=0,
-            string2='string')
-        model1_instance = MockModel1(**model_kwargs)
-        model2_instance = MockModel2(**model_kwargs)
-        storage.mock_model_1.put(model1_instance)
-        storage.mock_model_2.put(model2_instance)
-
-        instrument = self._track_changes({
-            MockModel1.dict1: dict,
-            MockModel1.list1: list,
-            MockModel1.int1: int,
-            MockModel1.string2: str,
-            MockModel2.dict2: dict,
-            MockModel2.list2: list,
-            MockModel2.int2: int,
-            MockModel2.name: str
-        })
-
-        assert not instrument.tracked_changes
-
-        storage_model1_instance = storage.mock_model_1.get(model1_instance.id)
-        storage_model2_instance = storage.mock_model_2.get(model2_instance.id)
-
-        storage_model1_instance.dict1 = {'hello': 'world'}
-        storage_model1_instance.dict2 = {'should': 'not track'}
-        storage_model1_instance.list1 = ['hello']
-        storage_model1_instance.list2 = ['should not track']
-        storage_model1_instance.int1 = 100
-        storage_model1_instance.int2 = 20000
-        storage_model1_instance.name = 'should not track'
-        storage_model1_instance.string2 = 'new_string'
-
-        storage_model2_instance.dict1.update({'should': 'not track'})
-        storage_model2_instance.dict2.update({'hello': 'world'})
-        storage_model2_instance.list1.append('should not track')
-        storage_model2_instance.list2.append('hello')
-        storage_model2_instance.int1 = 100
-        storage_model2_instance.int2 = 20000
-        storage_model2_instance.name = 'new_name'
-        storage_model2_instance.string2 = 'should not track'
-
-        assert instrument.tracked_changes == {
-            'mock_model_1': {
-                model1_instance.id: {
-                    'dict1': Value(STUB, {'hello': 'world'}),
-                    'list1': Value(STUB, ['hello']),
-                    'int1': Value(STUB, 100),
-                    'string2': Value(STUB, 'new_string')
-                }
-            },
-            'mock_model_2': {
-                model2_instance.id: {
-                    'dict2': Value({'initial': 'value'}, {'hello': 'world', 'initial': 'value'}),
-                    'list2': Value(['initial'], ['initial', 'hello']),
-                    'int2': Value(STUB, 20000),
-                    'name': Value(STUB, 'new_name'),
-                }
-            }
-        }
-
-    def test_attribute_initial_none_value(self, storage):
-        instance1 = MockModel1(name='name1', dict1=None)
-        instance2 = MockModel1(name='name2', dict1=None)
-        storage.mock_model_1.put(instance1)
-        storage.mock_model_1.put(instance2)
-        instrument = self._track_changes({MockModel1.dict1: dict})
-        instance1 = storage.mock_model_1.get(instance1.id)
-        instance2 = storage.mock_model_1.get(instance2.id)
-        instance1.dict1 = {'new': 'value'}
-        assert instrument.tracked_changes == {
-            'mock_model_1': {
-                instance1.id: {'dict1': Value(STUB, {'new': 'value'})},
-                instance2.id: {'dict1': Value(None, None)},
-            }
-        }
-
-    def test_attribute_set_none_value(self, storage):
-        instance = MockModel1(name='name')
-        storage.mock_model_1.put(instance)
-        instrument = self._track_changes({
-            MockModel1.dict1: dict,
-            MockModel1.list1: list,
-            MockModel1.string2: str,
-            MockModel1.int1: int
-        })
-        instance = storage.mock_model_1.get(instance.id)
-        instance.dict1 = None
-        instance.list1 = None
-        instance.string2 = None
-        instance.int1 = None
-        assert instrument.tracked_changes == {
-            'mock_model_1': {
-                instance.id: {
-                    'dict1': Value(STUB, None),
-                    'list1': Value(STUB, None),
-                    'string2': Value(STUB, None),
-                    'int1': Value(STUB, None)
-                }
-            }
-        }
-
-    def test_restore(self):
-        instrument = self._track_changes({MockModel1.dict1: dict})
-        # set instance attribute, load instance, refresh instance and flush_refresh listeners
-        assert len(instrument.listeners) == 4
-        for listener_args in instrument.listeners:
-            assert event.contains(*listener_args)
-        instrument.restore()
-        assert len(instrument.listeners) == 4
-        for listener_args in instrument.listeners:
-            assert not event.contains(*listener_args)
-        return instrument
-
-    def test_restore_twice(self):
-        instrument = self.test_restore()
-        instrument.restore()
-
-    def test_instrumentation_context_manager(self, storage):
-        instance = MockModel1(name='name')
-        storage.mock_model_1.put(instance)
-        with self._track_changes({MockModel1.dict1: dict}) as instrument:
-            instance = storage.mock_model_1.get(instance.id)
-            instance.dict1 = {'new': 'value'}
-            assert instrument.tracked_changes == {
-                'mock_model_1': {instance.id: {'dict1': Value(STUB, {'new': 'value'})}}
-            }
-            assert len(instrument.listeners) == 4
-            for listener_args in instrument.listeners:
-                assert event.contains(*listener_args)
-        for listener_args in instrument.listeners:
-            assert not event.contains(*listener_args)
-
-    def test_apply_tracked_changes(self, storage):
-        initial_values = {'dict1': {'initial': 'value'}, 'list1': ['initial']}
-        instance1_1 = MockModel1(name='instance1_1', **initial_values)
-        instance1_2 = MockModel1(name='instance1_2', **initial_values)
-        instance2_1 = MockModel2(name='instance2_1', **initial_values)
-        instance2_2 = MockModel2(name='instance2_2', **initial_values)
-        storage.mock_model_1.put(instance1_1)
-        storage.mock_model_1.put(instance1_2)
-        storage.mock_model_2.put(instance2_1)
-        storage.mock_model_2.put(instance2_2)
-
-        instrument = self._track_changes({
-            MockModel1.dict1: dict,
-            MockModel1.list1: list,
-            MockModel2.dict1: dict,
-            MockModel2.list1: list
-        })
-
-        def get_instances():
-            return (storage.mock_model_1.get(instance1_1.id),
-                    storage.mock_model_1.get(instance1_2.id),
-                    storage.mock_model_2.get(instance2_1.id),
-                    storage.mock_model_2.get(instance2_2.id))
-
-        instance1_1, instance1_2, instance2_1, instance2_2 = get_instances()
-        instance1_1.dict1 = {'new': 'value'}
-        instance1_2.list1 = ['new_value']
-        instance2_1.dict1.update({'new': 'value'})
-        instance2_2.list1.append('new_value')
-
-        instrument.restore()
-        storage.mock_model_1._session.expire_all()
-
-        instance1_1, instance1_2, instance2_1, instance2_2 = get_instances()
-        instance1_1.dict1 = {'overriding': 'value'}
-        instance1_2.list1 = ['overriding_value']
-        instance2_1.dict1 = {'overriding': 'value'}
-        instance2_2.list1 = ['overriding_value']
-        storage.mock_model_1.put(instance1_1)
-        storage.mock_model_1.put(instance1_2)
-        storage.mock_model_2.put(instance2_1)
-        storage.mock_model_2.put(instance2_2)
-        instance1_1, instance1_2, instance2_1, instance2_2 = get_instances()
-        assert instance1_1.dict1 == {'overriding': 'value'}
-        assert instance1_2.list1 == ['overriding_value']
-        assert instance2_1.dict1 == {'overriding': 'value'}
-        assert instance2_2.list1 == ['overriding_value']
-
-        instrumentation.apply_tracked_changes(
-            tracked_changes=instrument.tracked_changes,
-            new_instances={},
-            model=storage)
-
-        instance1_1, instance1_2, instance2_1, instance2_2 = get_instances()
-        assert instance1_1.dict1 == {'new': 'value'}
-        assert instance1_2.list1 == ['new_value']
-        assert instance2_1.dict1 == {'initial': 'value', 'new': 'value'}
-        assert instance2_2.list1 == ['initial', 'new_value']
-
-    def test_clear_instance(self, storage):
-        instance1 = MockModel1(name='name1')
-        instance2 = MockModel1(name='name2')
-        for instance in [instance1, instance2]:
-            storage.mock_model_1.put(instance)
-        instrument = self._track_changes({MockModel1.dict1: dict})
-        instance1.dict1 = {'new': 'value'}
-        instance2.dict1 = {'new2': 'value2'}
-        assert instrument.tracked_changes == {
-            'mock_model_1': {
-                instance1.id: {'dict1': Value(STUB, {'new': 'value'})},
-                instance2.id: {'dict1': Value(STUB, {'new2': 'value2'})}
-            }
-        }
-        instrument.clear(instance1)
-        assert instrument.tracked_changes == {
-            'mock_model_1': {
-                instance2.id: {'dict1': Value(STUB, {'new2': 'value2'})}
-            }
-        }
-
-    def test_clear_all(self, storage):
-        instance1 = MockModel1(name='name1')
-        instance2 = MockModel1(name='name2')
-        for instance in [instance1, instance2]:
-            storage.mock_model_1.put(instance)
-        instrument = self._track_changes({MockModel1.dict1: dict})
-        instance1.dict1 = {'new': 'value'}
-        instance2.dict1 = {'new2': 'value2'}
-        assert instrument.tracked_changes == {
-            'mock_model_1': {
-                instance1.id: {'dict1': Value(STUB, {'new': 'value'})},
-                instance2.id: {'dict1': Value(STUB, {'new2': 'value2'})}
-            }
-        }
-        instrument.clear()
-        assert instrument.tracked_changes == {}
-
-    def test_new_instances(self, storage):
-        model_kwargs = dict(
-            name='name',
-            dict1={'initial': 'value'},
-            dict2={'initial': 'value'},
-            list1=['initial'],
-            list2=['initial'],
-            int1=0,
-            int2=0,
-            string2='string')
-        model_instance_1 = MockModel1(**model_kwargs)
-        model_instance_2 = MockModel2(**model_kwargs)
-
-        instrument = self._track_changes(model=storage, instrumented_new=(MockModel1,))
-        assert not instrument.tracked_changes
-
-        storage.mock_model_1.put(model_instance_1)
-        storage.mock_model_2.put(model_instance_2)
-        # Assert all models made it to storage
-        assert len(storage.mock_model_1.list()) == len(storage.mock_model_2.list()) == 1
-
-        # Assert only one model was tracked
-        assert len(instrument.new_instances) == 1
-
-        mock_model_1 = instrument.new_instances[MockModel1.__tablename__].values()[0]
-        storage_model1_instance = storage.mock_model_1.get(model_instance_1.id)
-
-        for key in model_kwargs:
-            assert mock_model_1[key] == model_kwargs[key] == getattr(storage_model1_instance, key)
-
-    def _track_changes(self, instrumented_modified=None, model=None, instrumented_new=None):
-        instrument = instrumentation.track_changes(
-            model=model,
-            instrumented={'modified': instrumented_modified or {}, 'new': instrumented_new or {}})
-        instruments_holder.append(instrument)
-        return instrument
-
-    def test_track_changes_to_strict_dict(self, storage):
-        model_kwargs = dict(strict_dict={'key': 'value'},
-                            strict_list=['item'])
-        model_instance = StrictMockModel(**model_kwargs)
-        storage.strict_mock_model.put(model_instance)
-
-        instrument = self._track_changes({
-            StrictMockModel.strict_dict: dict,
-            StrictMockModel.strict_list: list,
-        })
-
-        assert not instrument.tracked_changes
-
-        storage_model_instance = storage.strict_mock_model.get(model_instance.id)
-
-        with pytest.raises(ValueFormatException):
-            storage_model_instance.strict_dict = {1: 1}
-
-        with pytest.raises(ValueFormatException):
-            storage_model_instance.strict_dict = {'hello': 1}
-
-        with pytest.raises(ValueFormatException):
-            storage_model_instance.strict_dict = {1: 'hello'}
-
-        storage_model_instance.strict_dict = {'hello': 'world'}
-        assert storage_model_instance.strict_dict == {'hello': 'world'}
-
-        with pytest.raises(ValueFormatException):
-            storage_model_instance.strict_list = [1]
-        storage_model_instance.strict_list = ['hello']
-        assert storage_model_instance.strict_list == ['hello']
-
-        assert instrument.tracked_changes == {
-            'strict_mock_model': {
-                model_instance.id: {
-                    'strict_dict': Value(STUB, {'hello': 'world'}),
-                    'strict_list': Value(STUB, ['hello']),
-                }
-            },
-        }
-
-
-@pytest.fixture(autouse=True)
-def restore_instrumentation():
-    yield
-    for instrument in instruments_holder:
-        instrument.restore()
-    del instruments_holder[:]
-
-
-@pytest.fixture
-def storage():
-    result = ModelStorage(api_cls=sql_mapi.SQLAlchemyModelAPI,
-                          items=(MockModel1, MockModel2, StrictMockModel),
-                          initiator=init_inmemory_model_storage)
-    yield result
-    release_sqlite_storage(result)
-
-
-class _MockModel(mixins.ModelMixin):
-    name = Column(Text)
-    dict1 = Column(modeling_types.Dict)
-    dict2 = Column(modeling_types.Dict)
-    list1 = Column(modeling_types.List)
-    list2 = Column(modeling_types.List)
-    int1 = Column(Integer)
-    int2 = Column(Integer)
-    string2 = Column(Text)
-
-
-class MockModel1(_MockModel, models.aria_declarative_base):
-    __tablename__ = 'mock_model_1'
-
-
-class MockModel2(_MockModel, models.aria_declarative_base):
-    __tablename__ = 'mock_model_2'
-
-
-class StrictMockModel(mixins.ModelMixin, models.aria_declarative_base):
-    __tablename__ = 'strict_mock_model'
-
-    strict_dict = Column(modeling_types.StrictDict(basestring, basestring))
-    strict_list = Column(modeling_types.StrictList(basestring))