X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=azure%2Faria%2Faria-extension-cloudify%2Fsrc%2Faria%2Faria%2Futils%2Fcaching.py;fp=azure%2Faria%2Faria-extension-cloudify%2Fsrc%2Faria%2Faria%2Futils%2Fcaching.py;h=5f8cd885da2229fd86c7a4741d42169fc8f5df43;hb=7409dfb144cf2a06210400134d822a1393462b1f;hp=0000000000000000000000000000000000000000;hpb=9e65649dfff8f00dc0a0ef6b10d020ae0e2255ba;p=multicloud%2Fazure.git 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 + `__. + """ + + 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()