Mercurial > piecrust2
comparison piecrust/cache.py @ 911:f2b75e4be981
internal: Use pickle for caching things on disk.
This is just easier and lets us use proper classes instead of converting
to/from dictionaries.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 23 Jul 2017 18:03:21 -0700 |
parents | d1095774bfcf |
children | cd0345e4001e |
comparison
equal
deleted
inserted
replaced
910:371731b555ec | 911:f2b75e4be981 |
---|---|
1 import os | 1 import os |
2 import os.path | 2 import os.path |
3 import json | |
4 import shutil | 3 import shutil |
5 import codecs | 4 import pickle |
6 import hashlib | 5 import hashlib |
7 import logging | 6 import logging |
8 import repoze.lru | 7 import repoze.lru |
9 | 8 |
10 | 9 |
82 def has(self, path): | 81 def has(self, path): |
83 cache_path = self.getCachePath(path) | 82 cache_path = self.getCachePath(path) |
84 return os.path.isfile(cache_path) | 83 return os.path.isfile(cache_path) |
85 | 84 |
86 def read(self, path): | 85 def read(self, path): |
87 cache_path = self.getCachePath(path) | 86 with self.openRead(path, mode='r', encoding='utf8') as fp: |
88 logger.debug("Reading cache: %s" % cache_path) | |
89 with codecs.open(cache_path, 'r', 'utf-8') as fp: | |
90 return fp.read() | 87 return fp.read() |
91 | 88 |
89 def openRead(self, path, mode='r', encoding=None): | |
90 cache_path = self.getCachePath(path) | |
91 return open(cache_path, mode=mode, encoding=encoding) | |
92 | |
92 def write(self, path, content): | 93 def write(self, path, content): |
94 with self.openWrite(path, mode='w', encoding='utf8') as fp: | |
95 fp.write(content) | |
96 | |
97 def openWrite(self, path, mode='w', encoding=None): | |
93 cache_path = self.getCachePath(path) | 98 cache_path = self.getCachePath(path) |
94 cache_dir = os.path.dirname(cache_path) | 99 cache_dir = os.path.dirname(cache_path) |
95 if not os.path.isdir(cache_dir): | 100 if not os.path.isdir(cache_dir): |
96 os.makedirs(cache_dir, 0o755) | 101 os.makedirs(cache_dir, 0o755) |
97 logger.debug("Writing cache: %s" % cache_path) | 102 return open(cache_path, mode=mode, encoding=encoding) |
98 with codecs.open(cache_path, 'w', 'utf-8') as fp: | |
99 fp.write(content) | |
100 | 103 |
101 def getCachePath(self, path): | 104 def getCachePath(self, path): |
102 if path.startswith('.'): | 105 if path.startswith('.'): |
103 path = '__index__' + path | 106 path = '__index__' + path |
104 return os.path.join(self.base_dir, path) | 107 return os.path.join(self.base_dir, path) |
152 return hashlib.md5(key.encode('utf8')).hexdigest() | 155 return hashlib.md5(key.encode('utf8')).hexdigest() |
153 | 156 |
154 | 157 |
155 class MemCache(object): | 158 class MemCache(object): |
156 """ Simple memory cache. It can be backed by a simple file-system | 159 """ Simple memory cache. It can be backed by a simple file-system |
157 cache, but items need to be JSON-serializable to do this. | 160 cache, but items need to be pickle-able to do this. |
158 """ | 161 """ |
159 def __init__(self, size=2048): | 162 def __init__(self, size=2048): |
160 self.cache = repoze.lru.LRUCache(size) | 163 self.cache = repoze.lru.LRUCache(size) |
161 self.fs_cache = None | 164 self.fs_cache = None |
162 self._last_access_hit = None | 165 self._last_access_hit = None |
179 | 182 |
180 def put(self, key, item, save_to_fs=True): | 183 def put(self, key, item, save_to_fs=True): |
181 self.cache.put(key, item) | 184 self.cache.put(key, item) |
182 if self.fs_cache and save_to_fs: | 185 if self.fs_cache and save_to_fs: |
183 fs_key = _make_fs_cache_key(key) | 186 fs_key = _make_fs_cache_key(key) |
184 item_raw = json.dumps(item) | 187 with self.fs_cache.openWrite(fs_key, mode='wb') as fp: |
185 self.fs_cache.write(fs_key, item_raw) | 188 pickle.dump(item, fp, pickle.HIGHEST_PROTOCOL) |
186 | 189 |
187 def get(self, key, item_maker, fs_cache_time=None, save_to_fs=True): | 190 def get(self, key, item_maker, fs_cache_time=None, save_to_fs=True): |
188 self._last_access_hit = True | 191 self._last_access_hit = True |
189 item = self.cache.get(key) | 192 item = self.cache.get(key) |
190 if item is not None: | 193 if item is not None: |
199 | 202 |
200 # Try first from the file-system cache. | 203 # Try first from the file-system cache. |
201 fs_key = _make_fs_cache_key(key) | 204 fs_key = _make_fs_cache_key(key) |
202 if (fs_key not in self._invalidated_fs_items and | 205 if (fs_key not in self._invalidated_fs_items and |
203 self.fs_cache.isValid(fs_key, fs_cache_time)): | 206 self.fs_cache.isValid(fs_key, fs_cache_time)): |
204 logger.debug("'%s' found in file-system cache." % | 207 logger.debug("'%s' found in file-system cache." % key) |
205 key) | 208 with self.fs_cache.openRead(fs_key, mode='rb') as fp: |
206 item_raw = self.fs_cache.read(fs_key) | 209 item = pickle.load(fp) |
207 item = json.loads(item_raw) | |
208 self.cache.put(key, item) | 210 self.cache.put(key, item) |
209 self._hits += 1 | 211 self._hits += 1 |
210 return item | 212 return item |
211 | 213 |
212 # Look into the mem-cache. | 214 # Look into the mem-cache. |
217 self._misses += 1 | 219 self._misses += 1 |
218 self._missed_keys.append(key) | 220 self._missed_keys.append(key) |
219 | 221 |
220 # Save to the file-system if needed. | 222 # Save to the file-system if needed. |
221 if self.fs_cache is not None and save_to_fs: | 223 if self.fs_cache is not None and save_to_fs: |
222 item_raw = json.dumps(item) | 224 with self.fs_cache.openWrite(fs_key, mode='wb') as fp: |
223 self.fs_cache.write(fs_key, item_raw) | 225 pickle.dump(item, fp, pickle.HIGHEST_PROTOCOL) |
224 | 226 |
225 return item | 227 return item |
226 | 228 |
227 |