PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/celery/utils/__init__.py

https://github.com/eldondev/celery
Python | 413 lines | 406 code | 7 blank | 0 comment | 0 complexity | ed4096aae561c1544b1c052f1d7466c2 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. from __future__ import generators
  2. import os
  3. import sys
  4. import operator
  5. import importlib
  6. import logging
  7. import threading
  8. import traceback
  9. import warnings
  10. from inspect import getargspec
  11. from itertools import islice
  12. from pprint import pprint
  13. from kombu.utils import gen_unique_id, rpartition, cached_property
  14. from celery.utils.compat import StringIO
  15. from celery.utils.functional import partial, wraps
  16. LOG_LEVELS = dict(logging._levelNames)
  17. LOG_LEVELS["FATAL"] = logging.FATAL
  18. LOG_LEVELS[logging.FATAL] = "FATAL"
  19. PENDING_DEPRECATION_FMT = """
  20. %(description)s is scheduled for deprecation in \
  21. version %(deprecation)s and removal in version v%(removal)s. \
  22. %(alternative)s
  23. """
  24. DEPRECATION_FMT = """
  25. %(description)s is deprecated and scheduled for removal in
  26. version %(removal)s. %(alternative)s
  27. """
  28. def deprecated(description=None, deprecation=None, removal=None,
  29. alternative=None):
  30. def _inner(fun):
  31. @wraps(fun)
  32. def __inner(*args, **kwargs):
  33. ctx = {"description": description or get_full_cls_name(fun),
  34. "deprecation": deprecation, "removal": removal,
  35. "alternative": alternative}
  36. if deprecation is not None:
  37. w = PendingDeprecationWarning(PENDING_DEPRECATION_FMT % ctx)
  38. else:
  39. w = DeprecationWarning(DEPRECATION_FMT % ctx)
  40. warnings.warn(w)
  41. return fun(*args, **kwargs)
  42. return __inner
  43. return _inner
  44. def lpmerge(L, R):
  45. """Left precedent dictionary merge. Keeps values from `l`, if the value
  46. in `r` is :const:`None`."""
  47. return dict(L, **dict((k, v) for k, v in R.iteritems() if v is not None))
  48. class promise(object):
  49. """A promise.
  50. Evaluated when called or if the :meth:`evaluate` method is called.
  51. The function is evaluated on every access, so the value is not
  52. memoized (see :class:`mpromise`).
  53. Overloaded operations that will evaluate the promise:
  54. :meth:`__str__`, :meth:`__repr__`, :meth:`__cmp__`.
  55. """
  56. def __init__(self, fun, *args, **kwargs):
  57. self._fun = fun
  58. self._args = args
  59. self._kwargs = kwargs
  60. def __call__(self):
  61. return self.evaluate()
  62. def evaluate(self):
  63. return self._fun(*self._args, **self._kwargs)
  64. def __str__(self):
  65. return str(self())
  66. def __repr__(self):
  67. return repr(self())
  68. def __cmp__(self, rhs):
  69. if isinstance(rhs, self.__class__):
  70. return -cmp(rhs, self())
  71. return cmp(self(), rhs)
  72. def __eq__(self, rhs):
  73. return self() == rhs
  74. def __deepcopy__(self, memo):
  75. memo[id(self)] = self
  76. return self
  77. def __reduce__(self):
  78. return (self.__class__, (self._fun, ), {"_args": self._args,
  79. "_kwargs": self._kwargs})
  80. class mpromise(promise):
  81. """Memoized promise.
  82. The function is only evaluated once, every subsequent access
  83. will return the same value.
  84. .. attribute:: evaluated
  85. Set to to :const:`True` after the promise has been evaluated.
  86. """
  87. evaluated = False
  88. _value = None
  89. def evaluate(self):
  90. if not self.evaluated:
  91. self._value = super(mpromise, self).evaluate()
  92. self.evaluated = True
  93. return self._value
  94. def maybe_promise(value):
  95. """Evaluates if the value is a promise."""
  96. if isinstance(value, promise):
  97. return value.evaluate()
  98. return value
  99. def noop(*args, **kwargs):
  100. """No operation.
  101. Takes any arguments/keyword arguments and does nothing.
  102. """
  103. pass
  104. def kwdict(kwargs):
  105. """Make sure keyword arguments are not in unicode.
  106. This should be fixed in newer Python versions,
  107. see: http://bugs.python.org/issue4978.
  108. """
  109. return dict((key.encode("utf-8"), value)
  110. for key, value in kwargs.items())
  111. def first(predicate, iterable):
  112. """Returns the first element in `iterable` that `predicate` returns a
  113. :const:`True` value for."""
  114. for item in iterable:
  115. if predicate(item):
  116. return item
  117. def firstmethod(method):
  118. """Returns a functions that with a list of instances,
  119. finds the first instance that returns a value for the given method.
  120. The list can also contain promises (:class:`promise`.)
  121. """
  122. def _matcher(seq, *args, **kwargs):
  123. for cls in seq:
  124. try:
  125. answer = getattr(maybe_promise(cls), method)(*args, **kwargs)
  126. if answer is not None:
  127. return answer
  128. except AttributeError:
  129. pass
  130. return _matcher
  131. def chunks(it, n):
  132. """Split an iterator into chunks with `n` elements each.
  133. Examples
  134. # n == 2
  135. >>> x = chunks(iter([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 2)
  136. >>> list(x)
  137. [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10]]
  138. # n == 3
  139. >>> x = chunks(iter([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), 3)
  140. >>> list(x)
  141. [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
  142. """
  143. for first in it:
  144. yield [first] + list(islice(it, n - 1))
  145. def padlist(container, size, default=None):
  146. """Pad list with default elements.
  147. Examples:
  148. >>> first, last, city = padlist(["George", "Costanza", "NYC"], 3)
  149. ("George", "Costanza", "NYC")
  150. >>> first, last, city = padlist(["George", "Costanza"], 3)
  151. ("George", "Costanza", None)
  152. >>> first, last, city, planet = padlist(["George", "Costanza",
  153. "NYC"], 4, default="Earth")
  154. ("George", "Costanza", "NYC", "Earth")
  155. """
  156. return list(container)[:size] + [default] * (size - len(container))
  157. def is_iterable(obj):
  158. try:
  159. iter(obj)
  160. except TypeError:
  161. return False
  162. return True
  163. def mattrgetter(*attrs):
  164. """Like :func:`operator.itemgetter` but returns :const:`None` on missing
  165. attributes instead of raising :exc:`AttributeError`."""
  166. return lambda obj: dict((attr, getattr(obj, attr, None))
  167. for attr in attrs)
  168. def get_full_cls_name(cls):
  169. """With a class, get its full module and class name."""
  170. return ".".join([cls.__module__,
  171. cls.__name__])
  172. def fun_takes_kwargs(fun, kwlist=[]):
  173. """With a function, and a list of keyword arguments, returns arguments
  174. in the list which the function takes.
  175. If the object has an `argspec` attribute that is used instead
  176. of using the :meth:`inspect.getargspec` introspection.
  177. :param fun: The function to inspect arguments of.
  178. :param kwlist: The list of keyword arguments.
  179. Examples
  180. >>> def foo(self, x, y, logfile=None, loglevel=None):
  181. ... return x * y
  182. >>> fun_takes_kwargs(foo, ["logfile", "loglevel", "task_id"])
  183. ["logfile", "loglevel"]
  184. >>> def foo(self, x, y, **kwargs):
  185. >>> fun_takes_kwargs(foo, ["logfile", "loglevel", "task_id"])
  186. ["logfile", "loglevel", "task_id"]
  187. """
  188. argspec = getattr(fun, "argspec", getargspec(fun))
  189. args, _varargs, keywords, _defaults = argspec
  190. if keywords != None:
  191. return kwlist
  192. return filter(partial(operator.contains, args), kwlist)
  193. def get_cls_by_name(name, aliases={}, imp=None):
  194. """Get class by name.
  195. The name should be the full dot-separated path to the class::
  196. modulename.ClassName
  197. Example::
  198. celery.concurrency.processes.TaskPool
  199. ^- class name
  200. If `aliases` is provided, a dict containing short name/long name
  201. mappings, the name is looked up in the aliases first.
  202. Examples:
  203. >>> get_cls_by_name("celery.concurrency.processes.TaskPool")
  204. <class 'celery.concurrency.processes.TaskPool'>
  205. >>> get_cls_by_name("default", {
  206. ... "default": "celery.concurrency.processes.TaskPool"})
  207. <class 'celery.concurrency.processes.TaskPool'>
  208. # Does not try to look up non-string names.
  209. >>> from celery.concurrency.processes import TaskPool
  210. >>> get_cls_by_name(TaskPool) is TaskPool
  211. True
  212. """
  213. if imp is None:
  214. imp = importlib.import_module
  215. if not isinstance(name, basestring):
  216. return name # already a class
  217. name = aliases.get(name) or name
  218. module_name, _, cls_name = rpartition(name, ".")
  219. try:
  220. module = imp(module_name)
  221. except ValueError, exc:
  222. raise ValueError("Couldn't import %r: %s" % (name, exc))
  223. return getattr(module, cls_name)
  224. get_symbol_by_name = get_cls_by_name
  225. def instantiate(name, *args, **kwargs):
  226. """Instantiate class by name.
  227. See :func:`get_cls_by_name`.
  228. """
  229. return get_cls_by_name(name)(*args, **kwargs)
  230. def truncate_text(text, maxlen=128, suffix="..."):
  231. """Truncates text to a maximum number of characters."""
  232. if len(text) >= maxlen:
  233. return text[:maxlen].rsplit(" ", 1)[0] + suffix
  234. return text
  235. def abbr(S, max, ellipsis="..."):
  236. if S is None:
  237. return "???"
  238. if len(S) > max:
  239. return ellipsis and (S[:max - len(ellipsis)] + ellipsis) or S[:max]
  240. return S
  241. def abbrtask(S, max):
  242. if S is None:
  243. return "???"
  244. if len(S) > max:
  245. module, _, cls = rpartition(S, ".")
  246. module = abbr(module, max - len(cls) - 3, False)
  247. return module + "[.]" + cls
  248. return S
  249. def isatty(fh):
  250. # Fixes bug with mod_wsgi:
  251. # mod_wsgi.Log object has no attribute isatty.
  252. return getattr(fh, "isatty", None) and fh.isatty()
  253. def textindent(t, indent=0):
  254. """Indent text."""
  255. return "\n".join(" " * indent + p for p in t.split("\n"))
  256. def import_from_cwd(module, imp=None):
  257. """Import module, but make sure it finds modules
  258. located in the current directory.
  259. Modules located in the current directory has
  260. precedence over modules located in `sys.path`.
  261. """
  262. if imp is None:
  263. imp = importlib.import_module
  264. cwd = os.getcwd()
  265. if cwd in sys.path:
  266. return imp(module)
  267. sys.path.insert(0, cwd)
  268. try:
  269. return imp(module)
  270. finally:
  271. try:
  272. sys.path.remove(cwd)
  273. except ValueError:
  274. pass
  275. def cry():
  276. """Return stacktrace of all active threads.
  277. From https://gist.github.com/737056
  278. """
  279. tmap = {}
  280. main_thread = None
  281. # get a map of threads by their ID so we can print their names
  282. # during the traceback dump
  283. for t in threading.enumerate():
  284. if getattr(t, "ident", None):
  285. tmap[t.ident] = t
  286. else:
  287. main_thread = t
  288. out = StringIO()
  289. for tid, frame in sys._current_frames().iteritems():
  290. thread = tmap.get(tid, main_thread)
  291. out.write("%s\n" % (thread.getName(), ))
  292. out.write("=================================================\n")
  293. traceback.print_stack(frame, file=out)
  294. out.write("=================================================\n")
  295. out.write("LOCAL VARIABLES\n")
  296. out.write("=================================================\n")
  297. pprint(frame.f_locals, stream=out)
  298. out.write("\n\n")
  299. return out.getvalue()