objects.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. # -*- coding: utf-8 -*-
  2. """
  3. celery.utils.objects
  4. ~~~~~~~~~~~~~~~~~~~~
  5. Object related utilities including introspection, etc.
  6. """
  7. from __future__ import absolute_import
  8. __all__ = ['mro_lookup']
  9. def mro_lookup(cls, attr, stop=(), monkey_patched=[]):
  10. """Return the first node by MRO order that defines an attribute.
  11. :keyword stop: A list of types that if reached will stop the search.
  12. :keyword monkey_patched: Use one of the stop classes if the attr's
  13. module origin is not in this list, this to detect monkey patched
  14. attributes.
  15. :returns None: if the attribute was not found.
  16. """
  17. for node in cls.mro():
  18. if node in stop:
  19. try:
  20. attr = node.__dict__[attr]
  21. module_origin = attr.__module__
  22. except (AttributeError, KeyError):
  23. pass
  24. else:
  25. if module_origin not in monkey_patched:
  26. return node
  27. return
  28. if attr in node.__dict__:
  29. return node
  30. class FallbackContext(object):
  31. """The built-in ``@contextmanager`` utility does not work well
  32. when wrapping other contexts, as the traceback is wrong when
  33. the wrapped context raises.
  34. This solves this problem and can be used instead of ``@contextmanager``
  35. in this example::
  36. @contextmanager
  37. def connection_or_default_connection(connection=None):
  38. if connection:
  39. # user already has a connection, should not close
  40. # after use
  41. yield connection
  42. else:
  43. # must have new connection, and also close the connection
  44. # after the block returns
  45. with create_new_connection() as connection:
  46. yield connection
  47. This wrapper can be used instead for the above like this::
  48. def connection_or_default_connection(connection=None):
  49. return FallbackContext(connection, create_new_connection)
  50. """
  51. def __init__(self, provided, fallback, *fb_args, **fb_kwargs):
  52. self.provided = provided
  53. self.fallback = fallback
  54. self.fb_args = fb_args
  55. self.fb_kwargs = fb_kwargs
  56. self._context = None
  57. def __enter__(self):
  58. if self.provided is not None:
  59. return self.provided
  60. context = self._context = self.fallback(
  61. *self.fb_args, **self.fb_kwargs
  62. ).__enter__()
  63. return context
  64. def __exit__(self, *exc_info):
  65. if self._context is not None:
  66. return self._context.__exit__(*exc_info)