Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1''' 

2The CacheFile object exposes a get, wrap and close interface to handlers. 

3 

4- ``.get()`` reads all data against the key 

5- ``.wrap(handler)`` is used to wrap the ``.write()`` method to append into a 

6 write queue, and the ``.on_finish()`` method to save the result. 

7 

8Each type of store has a separate CacheFile. (MemoryCacheFile, DiskCacheFile, 

9etc.) The parent CacheFile implements the no-caching behaviour. 

10 

11See gramex.handlers.BaseHandler for examples on how to use these objects. 

12''' 

13from six.moves import cPickle 

14from diskcache import Cache as DiskCache 

15from .ttlcache import TTLCache as MemoryCache 

16from gramex.config import app_log 

17from gramex.http import OK, NOT_MODIFIED 

18 

19# HTTP Headers that should not be cached 

20ignore_headers = { 

21 # Do not cache headers referenced anywhere in tornado.http1connection 

22 'Content-Encoding', 'Vary', 'Transfer-Encoding', 'Expect', 

23 'Keep-Alive', 'Connection', 'X-Consumed-Content-Encoding', 

24 # Do not cache things that SHOULD or WILL be recomputed anyway 

25 'Date', # This is the current date, not the Last-Modified date 

26 'Server', # Always show Gramex/version 

27 'Etag', # Automatically added by Tornado 

28 'Content-Length', # Automatically added by Tornado 

29} 

30 

31 

32def get_cachefile(store): 

33 if isinstance(store, MemoryCache): 33 ↛ 35line 33 didn't jump to line 35, because the condition on line 33 was never false

34 return MemoryCacheFile 

35 elif isinstance(store, DiskCache): 

36 return DiskCacheFile 

37 else: 

38 app_log.warning('cache: ignoring unknown store %s', store) 

39 return CacheFile 

40 

41 

42class CacheFile(object): 

43 

44 def __init__(self, key, store, handler, expire=None, statuses=None): 

45 self.key = key 

46 self.store = store 

47 self.handler = handler 

48 self.expire = expire 

49 self.statuses = statuses 

50 

51 def get(self): 

52 return None 

53 

54 def wrap(self, handler): 

55 return handler 

56 

57 

58class MemoryCacheFile(CacheFile): 

59 def get(self): 

60 result = self.store.get(self.key) 

61 return None if result is None else cPickle.loads(result) 

62 

63 def wrap(self, handler): 

64 self._finish = handler.finish 

65 

66 def finish(chunk=None): 

67 # Save the headers and body originally written 

68 headers = [[name, value] for name, value in handler._headers.get_all() 

69 if name not in ignore_headers] 

70 body = b''.join(handler._write_buffer) 

71 

72 # Call the original finish 

73 self._finish(chunk) 

74 

75 # Cache headers and body (only for allowed HTTP responses) 

76 status = handler.get_status() 

77 if status in self.statuses: 

78 self.store.set( 

79 key=self.key, 

80 value=cPickle.dumps({ 

81 'status': OK if status == NOT_MODIFIED else status, 

82 'headers': headers, 

83 'body': body, 

84 }, cPickle.HIGHEST_PROTOCOL), 

85 expire=self.expire, 

86 ) 

87 

88 handler.finish = finish 

89 

90 

91class DiskCacheFile(MemoryCacheFile): 

92 '''Identical interface to MemoryCacheFile''' 

93 pass