comparison piecrust/cache.py @ 416:ff6cc43fb40c

internal: Move `MemCache` to the `cache` module, remove threading locks.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 20 Jun 2015 21:05:25 -0700
parents c2ca72fb7f0b
children 5feb71d31a4f
comparison
equal deleted inserted replaced
415:0e9a94b7fdfa 416:ff6cc43fb40c
1 import os 1 import os
2 import os.path 2 import os.path
3 import json
3 import shutil 4 import shutil
4 import codecs 5 import codecs
6 import hashlib
5 import logging 7 import logging
6 import threading 8 import collections
9 import repoze.lru
7 10
8 11
9 logger = logging.getLogger(__name__) 12 logger = logging.getLogger(__name__)
10 13
11 14
12 class ExtensibleCache(object): 15 class ExtensibleCache(object):
13 def __init__(self, base_dir): 16 def __init__(self, base_dir):
14 self.base_dir = base_dir 17 self.base_dir = base_dir
15 self.lock = threading.Lock()
16 self.caches = {} 18 self.caches = {}
17 19
18 @property 20 @property
19 def enabled(self): 21 def enabled(self):
20 return True 22 return True
21 23
22 def getCache(self, name): 24 def getCache(self, name):
23 c = self.caches.get(name) 25 c = self.caches.get(name)
24 if c is None: 26 if c is None:
25 with self.lock: 27 c_dir = os.path.join(self.base_dir, name)
26 c = self.caches.get(name) 28 if not os.path.isdir(c_dir):
27 if c is None: 29 os.makedirs(c_dir, 0o755)
28 c_dir = os.path.join(self.base_dir, name) 30
29 if not os.path.isdir(c_dir): 31 c = SimpleCache(c_dir)
30 os.makedirs(c_dir, 0o755) 32 self.caches[name] = c
31
32 c = SimpleCache(c_dir)
33 self.caches[name] = c
34 return c 33 return c
35 34
36 def getCacheDir(self, name): 35 def getCacheDir(self, name):
37 return os.path.join(self.base_dir, name) 36 return os.path.join(self.base_dir, name)
38 37
143 pass 142 pass
144 143
145 def clearCaches(self, except_names=None): 144 def clearCaches(self, except_names=None):
146 pass 145 pass
147 146
147
148 def _make_fs_cache_key(key):
149 return hashlib.md5(key.encode('utf8')).hexdigest()
150
151
152 class MemCache(object):
153 """ Simple memory cache. It can be backed by a simple file-system
154 cache, but items need to be JSON-serializable to do this.
155 """
156 def __init__(self, size=2048):
157 self.cache = repoze.lru.LRUCache(size)
158 self.fs_cache = None
159 self._last_access_hit = None
160 self._invalidated_fs_items = set()
161
162 @property
163 def last_access_hit(self):
164 return self._last_access_hit
165
166 def invalidate(self, key):
167 logger.debug("Invalidating cache item '%s'." % key)
168 self.cache.invalidate(key)
169 if self.fs_cache:
170 logger.debug("Invalidating FS cache item '%s'." % key)
171 fs_key = _make_fs_cache_key(key)
172 self._invalidated_fs_items.add(fs_key)
173
174 def put(self, key, item, save_to_fs=True):
175 self.cache.put(key, item)
176 if self.fs_cache and save_to_fs:
177 fs_key = _make_fs_cache_key(key)
178 item_raw = json.dumps(item)
179 self.fs_cache.write(fs_key, item_raw)
180
181 def get(self, key, item_maker, fs_cache_time=None, save_to_fs=True):
182 self._last_access_hit = True
183 item = self.cache.get(key)
184 if item is None:
185 if (self.fs_cache is not None and
186 fs_cache_time is not None):
187 # Try first from the file-system cache.
188 fs_key = _make_fs_cache_key(key)
189 if (fs_key not in self._invalidated_fs_items and
190 self.fs_cache.isValid(fs_key, fs_cache_time)):
191 logger.debug("'%s' found in file-system cache." %
192 key)
193 item_raw = self.fs_cache.read(fs_key)
194 item = json.loads(
195 item_raw,
196 object_pairs_hook=collections.OrderedDict)
197 self.cache.put(key, item)
198 return item
199
200 # Look into the mem-cache.
201 logger.debug("'%s' not found in cache, must build." % key)
202 item = item_maker()
203 self.cache.put(key, item)
204 self._last_access_hit = False
205
206 # Save to the file-system if needed.
207 if self.fs_cache is not None and save_to_fs:
208 item_raw = json.dumps(item)
209 self.fs_cache.write(fs_key, item_raw)
210
211 return item
212
213