objects.py 2.7 KB

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