summaryrefslogtreecommitdiffstats
path: root/azure/aria/aria-extension-cloudify/src/aria/aria/utils/caching.py
diff options
context:
space:
mode:
Diffstat (limited to 'azure/aria/aria-extension-cloudify/src/aria/aria/utils/caching.py')
-rw-r--r--azure/aria/aria-extension-cloudify/src/aria/aria/utils/caching.py137
1 files changed, 137 insertions, 0 deletions
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
index 0000000..5f8cd88
--- /dev/null
+++ b/azure/aria/aria-extension-cloudify/src/aria/aria/utils/caching.py
@@ -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()