cache.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. #-*- coding:utf-8 -*-
  2. '''from douban code, cool '''
  3. import inspect
  4. from functools import wraps
  5. import time
  6. try:
  7. import cPickle as pickle
  8. except:
  9. import pickle
  10. from .empty import Empty
  11. from .format import format
  12. from rrd.store import mc
  13. # some time consts for mc expire
  14. HALF_HOUR = 1800
  15. ONE_HOUR = 3600
  16. HALF_DAY = ONE_HOUR * 12
  17. ONE_DAY = ONE_HOUR * 24
  18. ONE_WEEK = ONE_DAY * 7
  19. ONE_MONTH = ONE_DAY * 30
  20. def gen_key(key_pattern, arg_names, defaults, *a, **kw):
  21. return gen_key_factory(key_pattern, arg_names, defaults)(*a, **kw)
  22. def gen_key_factory(key_pattern, arg_names, defaults):
  23. args = dict(zip(arg_names[-len(defaults):], defaults)) if defaults else {}
  24. if callable(key_pattern):
  25. names = inspect.getargspec(key_pattern)[0]
  26. def gen_key(*a, **kw):
  27. aa = args.copy()
  28. aa.update(zip(arg_names, a))
  29. aa.update(kw)
  30. if callable(key_pattern):
  31. key = key_pattern(*[aa[n] for n in names])
  32. else:
  33. key = format(key_pattern, *[aa[n] for n in arg_names], **aa)
  34. return key and key.replace(' ','_'), aa
  35. return gen_key
  36. def cache_(key_pattern, mc, expire=0, max_retry=0):
  37. def deco(f):
  38. arg_names, varargs, varkw, defaults = inspect.getargspec(f)
  39. if varargs or varkw:
  40. raise Exception("do not support varargs")
  41. gen_key = gen_key_factory(key_pattern, arg_names, defaults)
  42. @wraps(f)
  43. def _(*a, **kw):
  44. key, args = gen_key(*a, **kw)
  45. if not key:
  46. return f(*a, **kw)
  47. if isinstance(key, unicode):
  48. key = key.encode("utf8")
  49. r = mc.get(key)
  50. # anti miss-storm
  51. retry = max_retry
  52. while r is None and retry > 0:
  53. time.sleep(0.1)
  54. r = mc.get(key)
  55. retry -= 1
  56. r = pickle.loads(r) if r else None
  57. if r is None:
  58. r = f(*a, **kw)
  59. if r is not None:
  60. mc.set(key, pickle.dumps(r), expire)
  61. if isinstance(r, Empty):
  62. r = None
  63. return r
  64. _.original_function = f
  65. return _
  66. return deco
  67. def pcache_(key_pattern, mc, count=300, expire=0, max_retry=0):
  68. def deco(f):
  69. arg_names, varargs, varkw, defaults = inspect.getargspec(f)
  70. if varargs or varkw:
  71. raise Exception("do not support varargs")
  72. if not ('limit' in arg_names):
  73. raise Exception("function must has 'limit' in args")
  74. gen_key = gen_key_factory(key_pattern, arg_names, defaults)
  75. @wraps(f)
  76. def _(*a, **kw):
  77. key, args = gen_key(*a, **kw)
  78. start = args.pop('start', 0)
  79. limit = args.pop('limit')
  80. start = int(start)
  81. limit = int(limit)
  82. if not key or limit is None or start+limit > count:
  83. return f(*a, **kw)
  84. if isinstance(key, unicode):
  85. key = key.encode("utf8")
  86. r = mc.get(key)
  87. # anti miss-storm
  88. retry = max_retry
  89. while r is None and retry > 0:
  90. time.sleep(0.1)
  91. r = mc.get(key)
  92. retry -= 1
  93. r = pickle.loads(r) if r else None
  94. if r is None:
  95. r = f(limit=count, **args)
  96. mc.set(key, pickle.dumps(r), expire)
  97. return r[start:start+limit]
  98. _.original_function = f
  99. return _
  100. return deco
  101. def delete_cache_(key_pattern, mc):
  102. def deco(f):
  103. arg_names, varargs, varkw, defaults = inspect.getargspec(f)
  104. if varargs or varkw:
  105. raise Exception("do not support varargs")
  106. gen_key = gen_key_factory(key_pattern, arg_names, defaults)
  107. @wraps(f)
  108. def _(*a, **kw):
  109. key, args = gen_key(*a, **kw)
  110. r = f(*a, **kw)
  111. mc.delete(key)
  112. return r
  113. return _
  114. _.original_function = f
  115. return deco
  116. def create_decorators(mc):
  117. def _cache(key_pattern, expire=0, mc=mc, max_retry=0):
  118. return cache_(key_pattern, mc, expire=expire, max_retry=max_retry)
  119. def _pcache(key_pattern, count=300, expire=0, max_retry=0):
  120. return pcache_(key_pattern, mc, count=count, expire=expire, max_retry=max_retry)
  121. def _delete_cache(key_pattern):
  122. return delete_cache_(key_pattern, mc=mc)
  123. return dict(cache=_cache, pcache=_pcache, delete_cache=_delete_cache)
  124. globals().update(create_decorators(mc))