vFW and vDNS support added to azure-plugin
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / aria / utils / caching.py
diff --git a/azure/aria/aria-extension-cloudify/src/aria/aria/utils/caching.py b/azure/aria/aria-extension-cloudify/src/aria/aria/utils/caching.py
new file mode 100644 (file)
index 0000000..5f8cd88
--- /dev/null
@@ -0,0 +1,137 @@
+# 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.
+
+"""
+Caching utilities.
+"""
+
+from __future__ import absolute_import  # so we can import standard 'collections' and 'threading'
+
+from threading import Lock
+from functools import partial
+
+from .collections import OrderedDict
+
+
+class cachedmethod(object):  # pylint: disable=invalid-name
+    """
+    Decorator for caching method return values.
+
+    The implementation is thread-safe.
+
+    Supports ``cache_info`` to be compatible with Python 3's ``functools.lru_cache``. Note that the
+    statistics are combined for all instances of the class.
+
+    Won't use the cache if not called when bound to an object, allowing you to override the cache.
+
+    Adapted from `this solution
+    <http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/>`__.
+    """
+
+    ENABLED = True
+
+    def __init__(self, func):
+        self.__doc__ = func.__doc__
+        self.func = func
+        self.hits = 0
+        self.misses = 0
+        self.lock = Lock()
+
+    def cache_info(self):
+        with self.lock:
+            return (self.hits, self.misses, None, self.misses)
+
+    def reset_cache_info(self):
+        with self.lock:
+            self.hits = 0
+            self.misses = 0
+
+    def __get__(self, instance, owner):
+        if instance is None:
+            # Don't use cache if not bound to an object
+            # Note: This is also a way for callers to override the cache
+            return self.func
+        return partial(self, instance)
+
+    def __call__(self, *args, **kwargs):
+        if not self.ENABLED:
+            return self.func(*args, **kwargs)
+
+        instance = args[0]
+        if not hasattr(instance, '_method_cache'):
+            instance._method_cache = {}
+        method_cache = instance._method_cache
+
+        key = (self.func, args[1:], frozenset(kwargs.items()))
+
+        try:
+            with self.lock:
+                return_value = method_cache[key]
+                self.hits += 1
+        except KeyError:
+            return_value = self.func(*args, **kwargs)
+            with self.lock:
+                method_cache[key] = return_value
+                self.misses += 1
+            # Another thread may override our cache entry here, so we need to read
+            # it again to make sure all threads use the same return value
+            return_value = method_cache.get(key, return_value)
+
+        return return_value
+
+
+class HasCachedMethods(object):
+    """
+    Provides convenience methods for working with :class:`cachedmethod`.
+    """
+
+    def __init__(self, method_cache=None):
+        self._method_cache = method_cache or {}
+
+    @property
+    def _method_cache_info(self):
+        """
+        The cache infos of all cached methods.
+
+        :rtype: dict of str, 4-tuple
+        """
+
+        cached_info = OrderedDict()
+        for k, v in self.__class__.__dict__.iteritems():
+            if isinstance(v, property):
+                # The property getter might be cached
+                v = v.fget
+            if hasattr(v, 'cache_info'):
+                cached_info[k] = v.cache_info()
+        return cached_info
+
+    def _reset_method_cache(self):
+        """
+        Resets the caches of all cached methods.
+        """
+
+        if hasattr(self, '_method_cache'):
+            self._method_cache = {}
+
+        # Note: Another thread may already be storing entries in the cache here.
+        # But it's not a big deal! It only means that our cache_info isn't
+        # guaranteed to be accurate.
+
+        for entry in self.__class__.__dict__.itervalues():
+            if isinstance(entry, property):
+                # The property getter might be cached
+                entry = entry.fget
+            if hasattr(entry, 'reset_cache_info'):
+                entry.reset_cache_info()