vFW and vDNS support added to azure-plugin
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / aria / utils / caching.py
1 # Licensed to the Apache Software Foundation (ASF) under one or more
2 # contributor license agreements.  See the NOTICE file distributed with
3 # this work for additional information regarding copyright ownership.
4 # The ASF licenses this file to You under the Apache License, Version 2.0
5 # (the "License"); you may not use this file except in compliance with
6 # the License.  You may obtain a copy of the License at
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 """
17 Caching utilities.
18 """
19
20 from __future__ import absolute_import  # so we can import standard 'collections' and 'threading'
21
22 from threading import Lock
23 from functools import partial
24
25 from .collections import OrderedDict
26
27
28 class cachedmethod(object):  # pylint: disable=invalid-name
29     """
30     Decorator for caching method return values.
31
32     The implementation is thread-safe.
33
34     Supports ``cache_info`` to be compatible with Python 3's ``functools.lru_cache``. Note that the
35     statistics are combined for all instances of the class.
36
37     Won't use the cache if not called when bound to an object, allowing you to override the cache.
38
39     Adapted from `this solution
40     <http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/>`__.
41     """
42
43     ENABLED = True
44
45     def __init__(self, func):
46         self.__doc__ = func.__doc__
47         self.func = func
48         self.hits = 0
49         self.misses = 0
50         self.lock = Lock()
51
52     def cache_info(self):
53         with self.lock:
54             return (self.hits, self.misses, None, self.misses)
55
56     def reset_cache_info(self):
57         with self.lock:
58             self.hits = 0
59             self.misses = 0
60
61     def __get__(self, instance, owner):
62         if instance is None:
63             # Don't use cache if not bound to an object
64             # Note: This is also a way for callers to override the cache
65             return self.func
66         return partial(self, instance)
67
68     def __call__(self, *args, **kwargs):
69         if not self.ENABLED:
70             return self.func(*args, **kwargs)
71
72         instance = args[0]
73         if not hasattr(instance, '_method_cache'):
74             instance._method_cache = {}
75         method_cache = instance._method_cache
76
77         key = (self.func, args[1:], frozenset(kwargs.items()))
78
79         try:
80             with self.lock:
81                 return_value = method_cache[key]
82                 self.hits += 1
83         except KeyError:
84             return_value = self.func(*args, **kwargs)
85             with self.lock:
86                 method_cache[key] = return_value
87                 self.misses += 1
88             # Another thread may override our cache entry here, so we need to read
89             # it again to make sure all threads use the same return value
90             return_value = method_cache.get(key, return_value)
91
92         return return_value
93
94
95 class HasCachedMethods(object):
96     """
97     Provides convenience methods for working with :class:`cachedmethod`.
98     """
99
100     def __init__(self, method_cache=None):
101         self._method_cache = method_cache or {}
102
103     @property
104     def _method_cache_info(self):
105         """
106         The cache infos of all cached methods.
107
108         :rtype: dict of str, 4-tuple
109         """
110
111         cached_info = OrderedDict()
112         for k, v in self.__class__.__dict__.iteritems():
113             if isinstance(v, property):
114                 # The property getter might be cached
115                 v = v.fget
116             if hasattr(v, 'cache_info'):
117                 cached_info[k] = v.cache_info()
118         return cached_info
119
120     def _reset_method_cache(self):
121         """
122         Resets the caches of all cached methods.
123         """
124
125         if hasattr(self, '_method_cache'):
126             self._method_cache = {}
127
128         # Note: Another thread may already be storing entries in the cache here.
129         # But it's not a big deal! It only means that our cache_info isn't
130         # guaranteed to be accurate.
131
132         for entry in self.__class__.__dict__.itervalues():
133             if isinstance(entry, property):
134                 # The property getter might be cached
135                 entry = entry.fget
136             if hasattr(entry, 'reset_cache_info'):
137                 entry.reset_cache_info()