Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """This module provides a decorator to cache the results of a function. | 5 """This module provides a decorator to cache the results of a function. |
| 6 | 6 |
| 7 Examples: | 7 Examples: |
| 8 1. Decorate a function: | 8 1. Decorate a function: |
| 9 @cache_decorator.Cached() | 9 @cache_decorator.Cached() |
| 10 def Test(a): | 10 def Test(a): |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 32 | 32 |
| 33 d2 = Downloader('http://url', 5) | 33 d2 = Downloader('http://url', 5) |
| 34 d2.Download('path') # Returned the cached downloaded data. | 34 d2.Download('path') # Returned the cached downloaded data. |
| 35 """ | 35 """ |
| 36 | 36 |
| 37 import cStringIO | 37 import cStringIO |
| 38 import functools | 38 import functools |
| 39 import hashlib | 39 import hashlib |
| 40 import inspect | 40 import inspect |
| 41 import logging | 41 import logging |
| 42 import os | |
| 42 import pickle | 43 import pickle |
| 44 import threading | |
| 43 import zlib | 45 import zlib |
| 44 | 46 |
| 45 from google.appengine.api import memcache | 47 from google.appengine.api import memcache |
| 46 | 48 |
| 49 _DEFAULT_LOCAL_CACHE_DIR = os.path.join(os.path.expanduser('~'), | |
| 50 '.culprit_finder', 'local_cache') | |
| 51 | |
| 47 | 52 |
| 48 class Cacher(object): | 53 class Cacher(object): |
| 49 """An interface to cache and retrieve data. | 54 """An interface to cache and retrieve data. |
| 50 | 55 |
| 51 Subclasses should implement the Get/Set functions. | 56 Subclasses should implement the Get/Set functions. |
| 52 TODO: Add a Delete function (default to no-op) if needed later. | 57 TODO: Add a Delete function (default to no-op) if needed later. |
| 53 """ | 58 """ |
| 54 def Get(self, key): | 59 def Get(self, key): |
| 55 """Returns the cached data for the given key if available. | 60 """Returns the cached data for the given key if available. |
| 56 | 61 |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 126 num += 1 | 131 num += 1 |
| 127 | 132 |
| 128 all_data[key] = _CachedItemMetaData(num) | 133 all_data[key] = _CachedItemMetaData(num) |
| 129 else: | 134 else: |
| 130 all_data[key] = compressed_data | 135 all_data[key] = compressed_data |
| 131 | 136 |
| 132 keys_not_set = memcache.set_multi(all_data, time=expire_time) | 137 keys_not_set = memcache.set_multi(all_data, time=expire_time) |
| 133 return len(keys_not_set) == 0 | 138 return len(keys_not_set) == 0 |
| 134 | 139 |
| 135 | 140 |
| 141 class LocalCacher(Cacher): | |
| 142 """Cacher that uses local files to cache data.""" | |
| 143 | |
| 144 lock = threading.Lock() | |
| 145 | |
| 146 def __init__(self, cache_dir=_DEFAULT_LOCAL_CACHE_DIR): | |
|
stgao
2016/11/02 02:15:07
Can we avoid the default value, and let client pas
Sharu Jiang
2016/11/08 01:17:12
Done.
| |
| 147 self.cache_dir = cache_dir | |
| 148 if not os.path.exists(cache_dir): # pragma: no cover. | |
| 149 os.makedirs(cache_dir) | |
| 150 | |
| 151 def Get(self, key): | |
| 152 with LocalCacher.lock: | |
| 153 path = os.path.join(self.cache_dir, key) | |
| 154 if not os.path.exists(path): | |
| 155 return None | |
| 156 | |
| 157 try: | |
| 158 with open(path) as f: | |
| 159 return pickle.loads(zlib.decompress(f.read())) | |
| 160 except Exception as e: # pragma: no cover. | |
| 161 logging.error('Failed loading cache: %s', e) | |
| 162 return None | |
| 163 | |
| 164 def Set(self, key, data): # pylint: disable=W | |
| 165 with LocalCacher.lock: | |
| 166 try: | |
| 167 with open(os.path.join(self.cache_dir, key), 'wb') as f: | |
| 168 f.write(zlib.compress(pickle.dumps(data))) | |
| 169 except Exception as e: # pragma: no cover. | |
| 170 logging.error('Failed setting cache for key %s: %s', key, e) | |
| 171 | |
| 172 | |
| 136 def _DefaultKeyGenerator(func, args, kwargs): | 173 def _DefaultKeyGenerator(func, args, kwargs): |
| 137 """Generates a key from the function and arguments passed to it. | 174 """Generates a key from the function and arguments passed to it. |
| 138 | 175 |
| 139 Args: | 176 Args: |
| 140 func (function): An arbitrary function. | 177 func (function): An arbitrary function. |
| 141 args (list): Positional arguments passed to ``func``. | 178 args (list): Positional arguments passed to ``func``. |
| 142 kwargs (dict): Keyword arguments passed to ``func``. | 179 kwargs (dict): Keyword arguments passed to ``func``. |
| 143 | 180 |
| 144 Returns: | 181 Returns: |
| 145 A string to represent a call to the given function with the given arguments. | 182 A string to represent a call to the given function with the given arguments. |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 219 except Exception: # pragma: no cover. | 256 except Exception: # pragma: no cover. |
| 220 logging.exception( | 257 logging.exception( |
| 221 'Failed to cache data for function %s.%s, args=%s, kwargs=%s', | 258 'Failed to cache data for function %s.%s, args=%s, kwargs=%s', |
| 222 func.__module__, func.__name__, repr(args), repr(kwargs)) | 259 func.__module__, func.__name__, repr(args), repr(kwargs)) |
| 223 | 260 |
| 224 return result | 261 return result |
| 225 | 262 |
| 226 return Wrapped | 263 return Wrapped |
| 227 | 264 |
| 228 return Decorator | 265 return Decorator |
| OLD | NEW |