comparison MoinMoin/support/werkzeug/contrib/cache.py @ 5801:8de563c487be

upgrade werkzeug to 0.8.1, document current bundled version and current minimum requirement (0.6, for py 2.7 compatibility)
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Thu, 01 Dec 2011 01:34:45 +0100
parents 246ba4eecab2
children fc1f97a47c7e
comparison
equal deleted inserted replaced
5800:4ab3c578e44b 5801:8de563c487be
17 ================= 17 =================
18 18
19 Caching is pretty simple. Basically you have a cache object lurking around 19 Caching is pretty simple. Basically you have a cache object lurking around
20 somewhere that is connected to a remote cache or the file system or 20 somewhere that is connected to a remote cache or the file system or
21 something else. When the request comes in you check if the current page 21 something else. When the request comes in you check if the current page
22 is already in the cache and if, you're returning it. Otherwise you generate 22 is already in the cache and if so, you're returning it from the cache.
23 the page and put it into the cache. (Or a fragment of the page, you don't 23 Otherwise you generate the page and put it into the cache. (Or a fragment
24 have to cache the full thing) 24 of the page, you don't have to cache the full thing)
25 25
26 Here a simple example of how to cache a sidebar for a template:: 26 Here is a simple example of how to cache a sidebar for a template::
27 27
28 def get_sidebar(user): 28 def get_sidebar(user):
29 identifier = 'sidebar_for/user%d' % user.id 29 identifier = 'sidebar_for/user%d' % user.id
30 value = cache.get(identifier) 30 value = cache.get(identifier)
31 if value is not None: 31 if value is not None:
36 36
37 Creating a Cache Object 37 Creating a Cache Object
38 ======================= 38 =======================
39 39
40 To create a cache object you just import the cache system of your choice 40 To create a cache object you just import the cache system of your choice
41 from the cache module and instanciate it. Then you can start working 41 from the cache module and instantiate it. Then you can start working
42 with that object: 42 with that object:
43 43
44 >>> from werkzeug.contrib.cache import SimpleCache 44 >>> from werkzeug.contrib.cache import SimpleCache
45 >>> c = SimpleCache() 45 >>> c = SimpleCache()
46 >>> c.set("foo", "value") 46 >>> c.set("foo", "value")
48 'value' 48 'value'
49 >>> c.get("missing") is None 49 >>> c.get("missing") is None
50 True 50 True
51 51
52 Please keep in mind that you have to create the cache and put it somewhere 52 Please keep in mind that you have to create the cache and put it somewhere
53 you have access to it (either as a module global you can import or if you 53 you have access to it (either as a module global you can import or you just
54 put it onto your WSGI application). 54 put it into your WSGI application).
55 55
56 :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details. 56 :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
57 :license: BSD, see LICENSE for more details. 57 :license: BSD, see LICENSE for more details.
58 """ 58 """
59 import os 59 import os
60 import re 60 import re
61 import tempfile
61 try: 62 try:
62 from hashlib import md5 63 from hashlib import md5
63 except ImportError: 64 except ImportError:
64 from md5 import new as md5 65 from md5 import new as md5
65 from itertools import izip 66 from itertools import izip
66 from time import time 67 from time import time
67 from cPickle import loads, dumps, load, dump, HIGHEST_PROTOCOL 68 from werkzeug.posixemulation import rename
69
70 try:
71 import cPickle as pickle
72 except ImportError:
73 import pickle
74
75
76 def _items(mappingorseq):
77 """Wrapper for efficient iteration over mappings represented by dicts
78 or sequences::
79
80 >>> for k, v in _items((i, i*i) for i in xrange(5)):
81 ... assert k*k == v
82
83 >>> for k, v in _items(dict((i, i*i) for i in xrange(5))):
84 ... assert k*k == v
85
86 """
87 return mappingorseq.iteritems() if hasattr(mappingorseq, 'iteritems') \
88 else mappingorseq
68 89
69 90
70 class BaseCache(object): 91 class BaseCache(object):
71 """Baseclass for the cache systems. All the cache systems implement this 92 """Baseclass for the cache systems. All the cache systems implement this
72 API or a superset of it. 93 API or a superset of it.
77 98
78 def __init__(self, default_timeout=300): 99 def __init__(self, default_timeout=300):
79 self.default_timeout = default_timeout 100 self.default_timeout = default_timeout
80 101
81 def get(self, key): 102 def get(self, key):
82 """Looks up key in the cache and returns it. If the key does not 103 """Looks up key in the cache and returns the value for it.
83 exist `None` is returned instead. 104 If the key does not exist `None` is returned instead.
84 105
85 :param key: the key to be looked up. 106 :param key: the key to be looked up.
86 """ 107 """
87 return None 108 return None
88 109
93 :param key: the key to delete. 114 :param key: the key to delete.
94 """ 115 """
95 pass 116 pass
96 117
97 def get_many(self, *keys): 118 def get_many(self, *keys):
98 """Returns a list of keys. For each key a item in the list is 119 """Returns a list of values for the given keys.
99 created. Example:: 120 For each key a item in the list is created. Example::
100 121
101 foo, bar = cache.get_many("foo", "bar") 122 foo, bar = cache.get_many("foo", "bar")
102 123
103 If a key can't be looked up `None` is returned for that key 124 If a key can't be looked up `None` is returned for that key
104 instead. 125 instead.
119 arguments. 140 arguments.
120 """ 141 """
121 return dict(izip(keys, self.get_many(*keys))) 142 return dict(izip(keys, self.get_many(*keys)))
122 143
123 def set(self, key, value, timeout=None): 144 def set(self, key, value, timeout=None):
124 """Adds or overrides a key in the cache. 145 """Adds a new key/value to the cache (overwrites value, if key already
146 exists in the cache).
147
148 :param key: the key to set
149 :param value: the value for the key
150 :param timeout: the cache timeout for the key (if not specified,
151 it uses the default timeout).
152 """
153 pass
154
155 def add(self, key, value, timeout=None):
156 """Works like :meth:`set` but does not overwrite the values of already
157 existing keys.
125 158
126 :param key: the key to set 159 :param key: the key to set
127 :param value: the value for the key 160 :param value: the value for the key
128 :param timeout: the cache timeout for the key or the default 161 :param timeout: the cache timeout for the key or the default
129 timeout if not specified. 162 timeout if not specified.
130 """ 163 """
131 pass 164 pass
132 165
133 def add(self, key, value, timeout=None):
134 """Works like :meth:`set` but does not override already existing
135 values.
136
137 :param key: the key to set
138 :param value: the value for the key
139 :param timeout: the cache timeout for the key or the default
140 timeout if not specified.
141 """
142 pass
143
144 def set_many(self, mapping, timeout=None): 166 def set_many(self, mapping, timeout=None):
145 """Sets multiple keys and values from a dict. 167 """Sets multiple keys and values from a mapping.
146 168
147 :param mapping: a dict with the values to set. 169 :param mapping: a mapping with the keys/values to set.
148 :param timeout: the cache timeout for the key or the default 170 :param timeout: the cache timeout for the key (if not specified,
149 timeout if not specified. 171 it uses the default timeout).
150 """ 172 """
151 for key, value in mapping.iteritems(): 173 for key, value in _items(mapping):
152 self.set(key, value, timeout) 174 self.set(key, value, timeout)
153 175
154 def delete_many(self, *keys): 176 def delete_many(self, *keys):
155 """Deletes multiple keys at once. 177 """Deletes multiple keys at once.
156 178
160 for key in keys: 182 for key in keys:
161 self.delete(key) 183 self.delete(key)
162 184
163 def clear(self): 185 def clear(self):
164 """Clears the cache. Keep in mind that not all caches support 186 """Clears the cache. Keep in mind that not all caches support
165 clearning of the full cache. 187 completely clearing the cache.
166 """ 188 """
167 pass 189 pass
168 190
169 def inc(self, key, delta=1): 191 def inc(self, key, delta=1):
170 """Increments the value of a key by `delta`. If the key does 192 """Increments the value of a key by `delta`. If the key does
224 246
225 def get(self, key): 247 def get(self, key):
226 now = time() 248 now = time()
227 expires, value = self._cache.get(key, (0, None)) 249 expires, value = self._cache.get(key, (0, None))
228 if expires > time(): 250 if expires > time():
229 return loads(value) 251 return pickle.loads(value)
230 252
231 def set(self, key, value, timeout=None): 253 def set(self, key, value, timeout=None):
232 if timeout is None: 254 if timeout is None:
233 timeout = self.default_timeout 255 timeout = self.default_timeout
234 self._prune() 256 self._prune()
235 self._cache[key] = (time() + timeout, dumps(value, HIGHEST_PROTOCOL)) 257 self._cache[key] = (time() + timeout, pickle.dumps(value,
258 pickle.HIGHEST_PROTOCOL))
236 259
237 def add(self, key, value, timeout=None): 260 def add(self, key, value, timeout=None):
238 if timeout is None: 261 if timeout is None:
239 timeout = self.default_timeout 262 timeout = self.default_timeout
240 if len(self._cache) > self._threshold: 263 if len(self._cache) > self._threshold:
241 self._prune() 264 self._prune()
242 item = (time() + timeout, dumps(value, HIGHEST_PROTOCOL)) 265 item = (time() + timeout, pickle.dumps(value,
266 pickle.HIGHEST_PROTOCOL))
243 self._cache.setdefault(key, item) 267 self._cache.setdefault(key, item)
244 268
245 def delete(self, key): 269 def delete(self, key):
246 self._cache.pop(key, None) 270 self._cache.pop(key, None)
247 271
249 _test_memcached_key = re.compile(r'[^\x00-\x21\xff]{1,250}$').match 273 _test_memcached_key = re.compile(r'[^\x00-\x21\xff]{1,250}$').match
250 274
251 class MemcachedCache(BaseCache): 275 class MemcachedCache(BaseCache):
252 """A cache that uses memcached as backend. 276 """A cache that uses memcached as backend.
253 277
254 The first argument can either be a list or tuple of server addresses 278 The first argument can either be an object that resembles the API of a
255 in which case Werkzeug tries to import the memcache module and connect 279 :class:`memcache.Client` or a tuple/list of server addresses. In the
256 to it, or an object that resembles the API of a :class:`memcache.Client`. 280 event that a tuple/list is passed, Werkzeug tries to import the best
281 available memcache library.
257 282
258 Implementation notes: This cache backend works around some limitations in 283 Implementation notes: This cache backend works around some limitations in
259 memcached to simplify the interface. For example unicode keys are encoded 284 memcached to simplify the interface. For example unicode keys are encoded
260 to utf-8 on the fly. Methods such as :meth:`~BaseCache.get_dict` return 285 to utf-8 on the fly. Methods such as :meth:`~BaseCache.get_dict` return
261 the keys in the same format as passed. Furthermore all get methods 286 the keys in the same format as passed. Furthermore all get methods
271 applications. Keep in mind that 296 applications. Keep in mind that
272 :meth:`~BaseCache.clear` will also clear keys with a 297 :meth:`~BaseCache.clear` will also clear keys with a
273 different prefix. 298 different prefix.
274 """ 299 """
275 300
276 def __init__(self, servers, default_timeout=300, key_prefix=None): 301 def __init__(self, servers=None, default_timeout=300, key_prefix=None):
277 BaseCache.__init__(self, default_timeout) 302 BaseCache.__init__(self, default_timeout)
278 if isinstance(servers, (list, tuple)): 303 if servers is None or isinstance(servers, (list, tuple)):
279 try: 304 if servers is None:
280 import cmemcache as memcache 305 servers = ['127.0.0.1:11211']
281 is_cmemcache = True 306 self._client = self.import_preferred_memcache_lib(servers)
282 except ImportError: 307 if self._client is None:
283 try: 308 raise RuntimeError('no memcache module found')
284 import memcache
285 is_cmemcache = False
286 except ImportError:
287 raise RuntimeError('no memcache module found')
288
289 # cmemcache has a bug that debuglog is not defined for the
290 # client. Whenever pickle fails you get a weird AttributError.
291 if is_cmemcache:
292 client = memcache.Client(map(str, servers))
293 try:
294 client.debuglog = lambda *a: None
295 except:
296 pass
297 else:
298 client = memcache.Client(servers, False, HIGHEST_PROTOCOL)
299 else: 309 else:
300 client = servers 310 # NOTE: servers is actually an already initialized memcache
301 311 # client.
302 self._client = client 312 self._client = servers
313
303 self.key_prefix = key_prefix 314 self.key_prefix = key_prefix
304 315
305 def get(self, key): 316 def get(self, key):
306 if isinstance(key, unicode): 317 if isinstance(key, unicode):
307 key = key.encode('utf-8') 318 key = key.encode('utf-8')
314 return self._client.get(key) 325 return self._client.get(key)
315 326
316 def get_dict(self, *keys): 327 def get_dict(self, *keys):
317 key_mapping = {} 328 key_mapping = {}
318 have_encoded_keys = False 329 have_encoded_keys = False
319 for idx, key in enumerate(keys): 330 for key in keys:
320 if isinstance(key, unicode): 331 if isinstance(key, unicode):
321 encoded_key = key.encode('utf-8') 332 encoded_key = key.encode('utf-8')
322 have_encoded_keys = True 333 have_encoded_keys = True
323 else: 334 else:
324 encoded_key = key 335 encoded_key = key
325 if self.key_prefix: 336 if self.key_prefix:
326 encoded_key = self.key_prefix + encoded_key 337 encoded_key = self.key_prefix + encoded_key
327 if _test_memcached_key(key): 338 if _test_memcached_key(key):
328 key_mapping[encoded_key] = key 339 key_mapping[encoded_key] = key
329 # the keys call here is important because otherwise cmemcache
330 # does ugly things. What exaclty I don't know, i think it does
331 # Py_DECREF but quite frankly i don't care.
332 d = rv = self._client.get_multi(key_mapping.keys()) 340 d = rv = self._client.get_multi(key_mapping.keys())
333 if have_encoded_keys or self.key_prefix: 341 if have_encoded_keys or self.key_prefix:
334 rv = {} 342 rv = {}
335 for key, value in d.iteritems(): 343 for key, value in d.iteritems():
336 rv[key_mapping[key]] = value 344 rv[key_mapping[key]] = value
364 372
365 def set_many(self, mapping, timeout=None): 373 def set_many(self, mapping, timeout=None):
366 if timeout is None: 374 if timeout is None:
367 timeout = self.default_timeout 375 timeout = self.default_timeout
368 new_mapping = {} 376 new_mapping = {}
369 for key, value in mapping.iteritems(): 377 for key, value in _items(mapping):
370 if isinstance(key, unicode): 378 if isinstance(key, unicode):
371 key = key.encode('utf-8') 379 key = key.encode('utf-8')
372 if self.key_prefix: 380 if self.key_prefix:
373 key = self.key_prefix + key 381 key = self.key_prefix + key
374 new_mapping[key] = value 382 new_mapping[key] = value
408 key = key.encode('utf-8') 416 key = key.encode('utf-8')
409 if self.key_prefix: 417 if self.key_prefix:
410 key = self.key_prefix + key 418 key = self.key_prefix + key
411 self._client.decr(key, delta) 419 self._client.decr(key, delta)
412 420
413 421 def import_preferred_memcache_lib(self, servers):
414 class GAEMemcachedCache(MemcachedCache): 422 """Returns an initialized memcache client. Used by the constructor."""
415 """Connects to the Google appengine memcached Cache. 423 try:
416 424 import pylibmc
425 except ImportError:
426 pass
427 else:
428 return pylibmc.Client(servers)
429
430 try:
431 from google.appengine.api import memcache
432 except ImportError:
433 pass
434 else:
435 return memcache.Client()
436
437 try:
438 import memcache
439 except ImportError:
440 pass
441 else:
442 return memcache.Client(servers)
443
444
445 # backwards compatibility
446 GAEMemcachedCache = MemcachedCache
447
448
449 class RedisCache(BaseCache):
450 """Uses the Redis key-value store as a cache backend.
451
452 The first argument can be either a string denoting address of the Redis
453 server or an object resembling an instance of a redis.Redis class.
454
455 Note: Python Redis API already takes care of encoding unicode strings on
456 the fly.
457
458 .. versionadded:: 0.7
459
460 .. versionadded:: 0.8
461 `key_prefix` was added.
462
463 .. versionchanged:: 0.8
464 This cache backend now properly serializes objects.
465
466 :param host: address of the Redis server or an object which API is
467 compatible with the official Python Redis client (redis-py).
468 :param port: port number on which Redis server listens for connections
417 :param default_timeout: the default timeout that is used if no timeout is 469 :param default_timeout: the default timeout that is used if no timeout is
418 specified on :meth:`~BaseCache.set`. 470 specified on :meth:`~BaseCache.set`.
419 :param key_prefix: a prefix that is added before all keys. This makes it 471 :param key_prefix: A prefix that should be added to all keys.
420 possible to use the same memcached server for different
421 applications. Keep in mind that
422 :meth:`~BaseCache.clear` will also clear keys with a
423 different prefix.
424 """ 472 """
425 473
426 def __init__(self, default_timeout=300, key_prefix=None): 474 def __init__(self, host='localhost', port=6379, default_timeout=300,
427 from google.appengine.api import memcache 475 key_prefix=None):
428 MemcachedCache.__init__(self, memcache.Client(), 476 BaseCache.__init__(self, default_timeout)
429 default_timeout, key_prefix) 477 if isinstance(host, basestring):
478 try:
479 import redis
480 except ImportError:
481 raise RuntimeError('no redis module found')
482 self._client = redis.Redis(host=host, port=port)
483 else:
484 self._client = host
485 self.key_prefix = key_prefix or ''
486
487 def dump_object(self, value):
488 """Dumps an object into a string for redis. By default it serializes
489 integers as regular string and pickle dumps everything else.
490 """
491 if isinstance(value, (int, long)):
492 return str(value)
493 return '!' + pickle.dumps(value)
494
495 def load_object(self, value):
496 """The reversal of :meth:`dump_object`. This might be callde with
497 None.
498 """
499 if value is None:
500 return None
501 if value.startswith('!'):
502 return pickle.loads(value[1:])
503 try:
504 return int(value)
505 except ValueError:
506 # before 0.8 we did not have serialization. Still support that.
507 return value
508
509 def get(self, key):
510 return self.load_object(self._client.get(self.key_prefix + key))
511
512 def get_many(self, *keys):
513 if self.key_prefix:
514 keys = [self.key_prefix + key for key in keys]
515 return [self.load_object(x) for x in self._client.mget(keys)]
516
517 def set(self, key, value, timeout=None):
518 if timeout is None:
519 timeout = self.default_timeout
520 dump = self.dump_object(value)
521 self._client.setex(self.key_prefix + key, dump, timeout)
522
523 def add(self, key, value, timeout=None):
524 if timeout is None:
525 timeout = self.default_timeout
526 dump = self.dump_object(value)
527 added = self._client.setnx(self.key_prefix + key, dump)
528 if added:
529 self._client.expire(self.key_prefix + key, timeout)
530
531 def set_many(self, mapping, timeout=None):
532 if timeout is None:
533 timeout = self.default_timeout
534 pipe = self._client.pipeline()
535 for key, value in _items(mapping):
536 dump = self.dump_object(value)
537 pipe.setex(self.key_prefix + key, dump, timeout)
538 pipe.execute()
539
540 def delete(self, key):
541 self._client.delete(self.key_prefix + key)
542
543 def delete_many(self, *keys):
544 if not keys:
545 return
546 if self.key_prefix:
547 keys = [self.key_prefix + key for key in keys]
548 self._client.delete(*keys)
549
550 def clear(self):
551 if self.key_prefix:
552 keys = self._client.keys(self.key_prefix + '*')
553 if keys:
554 self._client.delete(*keys)
555 else:
556 self._client.flushdb()
557
558 def inc(self, key, delta=1):
559 return self._client.incr(self.key_prefix + key, delta)
560
561 def dec(self, key, delta=1):
562 return self._client.decr(self.key_prefix + key, delta)
430 563
431 564
432 class FileSystemCache(BaseCache): 565 class FileSystemCache(BaseCache):
433 """A cache that stores the items on the file system. This cache depends 566 """A cache that stores the items on the file system. This cache depends
434 on being the only user of the `cache_dir`. Make absolutely sure that 567 on being the only user of the `cache_dir`. Make absolutely sure that
435 nobody but this cache stores files there or otherwise the chace will 568 nobody but this cache stores files there or otherwise the cache will
436 randomely delete files therein. 569 randomly delete files therein.
437 570
438 :param cache_dir: the directory where cached files are stored. 571 :param cache_dir: the directory where cache files are stored.
439 :param threshold: the maximum number of items the cache stores before 572 :param threshold: the maximum number of items the cache stores before
440 it starts deleting some. 573 it starts deleting some.
441 :param default_timeout: the default timeout that is used if no timeout is 574 :param default_timeout: the default timeout that is used if no timeout is
442 specified on :meth:`~BaseCache.set`. 575 specified on :meth:`~BaseCache.set`.
576 :param mode: the file mode wanted for the cache files, default 0600
443 """ 577 """
444 578
445 def __init__(self, cache_dir, threshold=500, default_timeout=300): 579 #: used for temporary files by the FileSystemCache
580 _fs_transaction_suffix = '.__wz_cache'
581
582 def __init__(self, cache_dir, threshold=500, default_timeout=300, mode=0600):
446 BaseCache.__init__(self, default_timeout) 583 BaseCache.__init__(self, default_timeout)
447 self._path = cache_dir 584 self._path = cache_dir
448 self._threshold = threshold 585 self._threshold = threshold
586 self._mode = mode
449 if not os.path.exists(self._path): 587 if not os.path.exists(self._path):
450 os.makedirs(self._path) 588 os.makedirs(self._path)
451 589
590 def _list_dir(self):
591 """return a list of (fully qualified) cache filenames
592 """
593 return [os.path.join(self._path, fn) for fn in os.listdir(self._path)
594 if not fn.endswith(self._fs_transaction_suffix)]
595
452 def _prune(self): 596 def _prune(self):
453 entries = os.listdir(self._path) 597 entries = self._list_dir()
454 if len(entries) > self._threshold: 598 if len(entries) > self._threshold:
455 now = time() 599 now = time()
456 for idx, key in enumerate(entries): 600 for idx, fname in enumerate(entries):
601 remove = False
602 f = None
457 try: 603 try:
458 f = file(self._get_filename(key)) 604 try:
459 if load(f) > now and idx % 3 != 0: 605 f = open(fname, 'rb')
460 f.close() 606 expires = pickle.load(f)
461 continue 607 remove = expires <= now or idx % 3 == 0
462 except: 608 finally:
463 f.close() 609 if f is not None:
464 self.delete(key) 610 f.close()
611 except Exception:
612 pass
613 if remove:
614 try:
615 os.remove(fname)
616 except (IOError, OSError):
617 pass
618
619 def clear(self):
620 for fname in self._list_dir():
621 try:
622 os.remove(fname)
623 except (IOError, OSError):
624 pass
465 625
466 def _get_filename(self, key): 626 def _get_filename(self, key):
467 hash = md5(key).hexdigest() 627 hash = md5(key).hexdigest()
468 return os.path.join(self._path, hash) 628 return os.path.join(self._path, hash)
469 629
470 def get(self, key): 630 def get(self, key):
471 filename = self._get_filename(key) 631 filename = self._get_filename(key)
472 try: 632 try:
473 f = file(filename, 'rb') 633 f = open(filename, 'rb')
474 try: 634 try:
475 if load(f) >= time(): 635 if pickle.load(f) >= time():
476 return load(f) 636 return pickle.load(f)
477 finally: 637 finally:
478 f.close() 638 f.close()
479 os.remove(filename) 639 os.remove(filename)
480 except: 640 except Exception:
481 return None 641 return None
482 642
483 def add(self, key, value, timeout=None): 643 def add(self, key, value, timeout=None):
484 filename = self._get_filename(key) 644 filename = self._get_filename(key)
485 if not os.path.exists(filename): 645 if not os.path.exists(filename):
489 if timeout is None: 649 if timeout is None:
490 timeout = self.default_timeout 650 timeout = self.default_timeout
491 filename = self._get_filename(key) 651 filename = self._get_filename(key)
492 self._prune() 652 self._prune()
493 try: 653 try:
494 f = file(filename, 'wb') 654 fd, tmp = tempfile.mkstemp(suffix=self._fs_transaction_suffix,
655 dir=self._path)
656 f = os.fdopen(fd, 'wb')
495 try: 657 try:
496 dump(int(time() + timeout), f, 1) 658 pickle.dump(int(time() + timeout), f, 1)
497 dump(value, f, HIGHEST_PROTOCOL) 659 pickle.dump(value, f, pickle.HIGHEST_PROTOCOL)
498 finally: 660 finally:
499 f.close() 661 f.close()
662 rename(tmp, filename)
663 os.chmod(filename, self._mode)
500 except (IOError, OSError): 664 except (IOError, OSError):
501 pass 665 pass
502 666
503 def delete(self, key): 667 def delete(self, key):
504 try: 668 try:
505 os.remove(self._get_filename(key)) 669 os.remove(self._get_filename(key))
506 except (IOError, OSError): 670 except (IOError, OSError):
507 pass 671 pass
508
509 def clear(self):
510 for key in os.listdir(self._path):
511 self.delete(key)