functional.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. # -*- coding: utf-8 -*-
  2. """
  3. celery.utils.functional
  4. ~~~~~~~~~~~~~~~~~~~~~~~
  5. Utilities for functions.
  6. :copyright: (c) 2009 - 2012 by Ask Solem.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. from __future__ import absolute_import
  10. from __future__ import with_statement
  11. import operator
  12. from functools import partial, wraps
  13. from itertools import islice
  14. from threading import Lock, RLock
  15. from kombu.utils import cached_property
  16. from kombu.utils.functional import promise, maybe_promise
  17. from .compat import UserDict, UserList, OrderedDict
  18. KEYWORD_MARK = object()
  19. is_not_None = partial(operator.is_not, None)
  20. class LRUCache(UserDict):
  21. """LRU Cache implementation using a doubly linked list to track access.
  22. :keyword limit: The maximum number of keys to keep in the cache.
  23. When a new key is inserted and the limit has been exceeded,
  24. the *Least Recently Used* key will be discarded from the
  25. cache.
  26. """
  27. def __init__(self, limit=None):
  28. self.limit = limit
  29. self.mutex = RLock()
  30. self.data = OrderedDict()
  31. def __getitem__(self, key):
  32. with self.mutex:
  33. value = self[key] = self.data.pop(key)
  34. return value
  35. def keys(self):
  36. # userdict.keys in py3k calls __getitem__
  37. return self.data.keys()
  38. def values(self):
  39. return list(self._iterate_values())
  40. def items(self):
  41. return list(self._iterate_items())
  42. def __setitem__(self, key, value):
  43. # remove least recently used key.
  44. with self.mutex:
  45. if self.limit and len(self.data) >= self.limit:
  46. self.data.pop(iter(self.data).next())
  47. self.data[key] = value
  48. def __iter__(self):
  49. return self.data.iterkeys()
  50. def _iterate_items(self):
  51. for k in self:
  52. try:
  53. yield (k, self.data[k])
  54. except KeyError: # pragma: no cover
  55. pass
  56. iteritems = _iterate_items
  57. def _iterate_values(self):
  58. for k in self:
  59. try:
  60. yield self.data[k]
  61. except KeyError: # pragma: no cover
  62. pass
  63. itervalues = _iterate_values
  64. def incr(self, key, delta=1):
  65. with self.mutex:
  66. # this acts as memcached does- store as a string, but return a
  67. # integer as long as it exists and we can cast it
  68. newval = int(self.data.pop(key)) + delta
  69. self[key] = str(newval)
  70. return newval
  71. def is_list(l):
  72. return hasattr(l, "__iter__") and not isinstance(l, dict)
  73. def maybe_list(l):
  74. return l if l is None or is_list(l) else [l]
  75. def memoize(maxsize=None, Cache=LRUCache):
  76. def _memoize(fun):
  77. mutex = Lock()
  78. cache = Cache(limit=maxsize)
  79. @wraps(fun)
  80. def _M(*args, **kwargs):
  81. key = args + (KEYWORD_MARK, ) + tuple(sorted(kwargs.iteritems()))
  82. try:
  83. with mutex:
  84. value = cache[key]
  85. except KeyError:
  86. value = fun(*args, **kwargs)
  87. _M.misses += 1
  88. with mutex:
  89. cache[key] = value
  90. else:
  91. _M.hits += 1
  92. return value
  93. def clear():
  94. """Clear the cache and reset cache statistics."""
  95. cache.clear()
  96. _M.hits = _M.misses = 0
  97. _M.hits = _M.misses = 0
  98. _M.clear = clear
  99. _M.original_func = fun
  100. return _M
  101. return _memoize
  102. class mpromise(promise):
  103. """Memoized promise.
  104. The function is only evaluated once, every subsequent access
  105. will return the same value.
  106. .. attribute:: evaluated
  107. Set to to :const:`True` after the promise has been evaluated.
  108. """
  109. evaluated = False
  110. _value = None
  111. def evaluate(self):
  112. if not self.evaluated:
  113. self._value = super(mpromise, self).evaluate()
  114. self.evaluated = True
  115. return self._value
  116. def noop(*args, **kwargs):
  117. """No operation.
  118. Takes any arguments/keyword arguments and does nothing.
  119. """
  120. pass
  121. def first(predicate, iterable):
  122. """Returns the first element in `iterable` that `predicate` returns a
  123. :const:`True` value for."""
  124. predicate = predicate or is_not_None
  125. for item in iterable:
  126. if predicate(item):
  127. return item
  128. def firstmethod(method):
  129. """Returns a function that with a list of instances,
  130. finds the first instance that returns a value for the given method.
  131. The list can also contain promises (:class:`promise`.)
  132. """
  133. def _matcher(it, *args, **kwargs):
  134. for obj in it:
  135. try:
  136. answer = getattr(maybe_promise(obj), method)(*args, **kwargs)
  137. except AttributeError:
  138. pass
  139. else:
  140. if answer is not None:
  141. return answer
  142. return _matcher
  143. def chunks(it, n):
  144. """Split an iterator into chunks with `n` elements each.
  145. Examples
  146. # n == 2
  147. >>> x = chunks(iter([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 2)
  148. >>> list(x)
  149. [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10]]
  150. # n == 3
  151. >>> x = chunks(iter([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 3)
  152. >>> list(x)
  153. [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
  154. """
  155. # XXX This function is not used anymore, at least not by Celery itself.
  156. for first in it:
  157. yield [first] + list(islice(it, n - 1))
  158. def padlist(container, size, default=None):
  159. """Pad list with default elements.
  160. Examples:
  161. >>> first, last, city = padlist(["George", "Costanza", "NYC"], 3)
  162. ("George", "Costanza", "NYC")
  163. >>> first, last, city = padlist(["George", "Costanza"], 3)
  164. ("George", "Costanza", None)
  165. >>> first, last, city, planet = padlist(["George", "Costanza",
  166. "NYC"], 4, default="Earth")
  167. ("George", "Costanza", "NYC", "Earth")
  168. """
  169. return list(container)[:size] + [default] * (size - len(container))
  170. def mattrgetter(*attrs):
  171. """Like :func:`operator.itemgetter` but returns :const:`None` on missing
  172. attributes instead of raising :exc:`AttributeError`."""
  173. return lambda obj: dict((attr, getattr(obj, attr, None))
  174. for attr in attrs)
  175. def uniq(it):
  176. seen = set()
  177. for obj in it:
  178. if obj not in seen:
  179. yield obj
  180. seen.add(obj)
  181. class _regen(UserList, list):
  182. # must be subclass of list so that json can encode.
  183. def __init__(self, it):
  184. self.__it = it
  185. @cached_property
  186. def data(self):
  187. return list(self.__it)
  188. def regen(it):
  189. if isinstance(it, (list, tuple)):
  190. return it
  191. return _regen(it)