PageRenderTime 26ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/celery/utils/__init__.py

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