abstract.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. """Object utilities."""
  2. from __future__ import absolute_import, unicode_literals
  3. from copy import copy
  4. from .connection import maybe_channel
  5. from .exceptions import NotBoundError
  6. from .five import python_2_unicode_compatible
  7. from .utils.functional import ChannelPromise
  8. __all__ = ('Object', 'MaybeChannelBound')
  9. def unpickle_dict(cls, kwargs):
  10. return cls(**kwargs)
  11. def _any(v):
  12. return v
  13. class Object(object):
  14. """Common base class.
  15. Supports automatic kwargs->attributes handling, and cloning.
  16. """
  17. attrs = ()
  18. def __init__(self, *args, **kwargs):
  19. for name, type_ in self.attrs:
  20. value = kwargs.get(name)
  21. if value is not None:
  22. setattr(self, name, (type_ or _any)(value))
  23. else:
  24. try:
  25. getattr(self, name)
  26. except AttributeError:
  27. setattr(self, name, None)
  28. def as_dict(self, recurse=False):
  29. def f(obj, type):
  30. if recurse and isinstance(obj, Object):
  31. return obj.as_dict(recurse=True)
  32. return type(obj) if type and obj is not None else obj
  33. return {
  34. attr: f(getattr(self, attr), type) for attr, type in self.attrs
  35. }
  36. def __reduce__(self):
  37. return unpickle_dict, (self.__class__, self.as_dict())
  38. def __copy__(self):
  39. return self.__class__(**self.as_dict())
  40. @python_2_unicode_compatible
  41. class MaybeChannelBound(Object):
  42. """Mixin for classes that can be bound to an AMQP channel."""
  43. _channel = None
  44. _is_bound = False
  45. #: Defines whether maybe_declare can skip declaring this entity twice.
  46. can_cache_declaration = False
  47. def __call__(self, channel):
  48. """`self(channel) -> self.bind(channel)`."""
  49. return self.bind(channel)
  50. def bind(self, channel):
  51. """Create copy of the instance that is bound to a channel."""
  52. return copy(self).maybe_bind(channel)
  53. def maybe_bind(self, channel):
  54. """Bind instance to channel if not already bound."""
  55. if not self.is_bound and channel:
  56. self._channel = maybe_channel(channel)
  57. self.when_bound()
  58. self._is_bound = True
  59. return self
  60. def revive(self, channel):
  61. """Revive channel after the connection has been re-established.
  62. Used by :meth:`~kombu.Connection.ensure`.
  63. """
  64. if self.is_bound:
  65. self._channel = channel
  66. self.when_bound()
  67. def when_bound(self):
  68. """Callback called when the class is bound."""
  69. pass
  70. def __repr__(self):
  71. return self._repr_entity(type(self).__name__)
  72. def _repr_entity(self, item=''):
  73. item = item or type(self).__name__
  74. if self.is_bound:
  75. return '<{0} bound to chan:{1}>'.format(
  76. item or type(self).__name__, self.channel.channel_id)
  77. return '<unbound {0}>'.format(item)
  78. @property
  79. def is_bound(self):
  80. """Flag set if the channel is bound."""
  81. return self._is_bound and self._channel is not None
  82. @property
  83. def channel(self):
  84. """Current channel if the object is bound."""
  85. channel = self._channel
  86. if channel is None:
  87. raise NotBoundError(
  88. "Can't call method on {0} not bound to a channel".format(
  89. type(self).__name__))
  90. if isinstance(channel, ChannelPromise):
  91. channel = self._channel = channel()
  92. return channel