Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

exchange.py 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. """
  2. kombu.transport.virtual.exchange
  3. ================================
  4. Implementations of the standard exchanges defined
  5. by the AMQ protocol (excluding the `headers` exchange).
  6. """
  7. from __future__ import absolute_import
  8. from kombu.utils import escape_regex
  9. import re
  10. class ExchangeType(object):
  11. """Implements the specifics for an exchange type.
  12. :param channel: AMQ Channel
  13. """
  14. type = None
  15. def __init__(self, channel):
  16. self.channel = channel
  17. def lookup(self, table, exchange, routing_key, default):
  18. """Lookup all queues matching `routing_key` in `exchange`.
  19. :returns: `default` if no queues matched.
  20. """
  21. raise NotImplementedError('subclass responsibility')
  22. def prepare_bind(self, queue, exchange, routing_key, arguments):
  23. """Return tuple of `(routing_key, regex, queue)` to be stored
  24. for bindings to this exchange."""
  25. return routing_key, None, queue
  26. def equivalent(self, prev, exchange, type,
  27. durable, auto_delete, arguments):
  28. """Return true if `prev` and `exchange` is equivalent."""
  29. return (type == prev['type'] and
  30. durable == prev['durable'] and
  31. auto_delete == prev['auto_delete'] and
  32. (arguments or {}) == (prev['arguments'] or {}))
  33. class DirectExchange(ExchangeType):
  34. """The `direct` exchange routes based on exact routing keys."""
  35. type = 'direct'
  36. def lookup(self, table, exchange, routing_key, default):
  37. return [queue for rkey, _, queue in table
  38. if rkey == routing_key]
  39. def deliver(self, message, exchange, routing_key, **kwargs):
  40. _lookup = self.channel._lookup
  41. _put = self.channel._put
  42. for queue in _lookup(exchange, routing_key):
  43. _put(queue, message, **kwargs)
  44. class TopicExchange(ExchangeType):
  45. """The `topic` exchange routes messages based on words separated by
  46. dots, using wildcard characters ``*`` (any single word), and ``#``
  47. (one or more words)."""
  48. type = 'topic'
  49. #: map of wildcard to regex conversions
  50. wildcards = {'*': r'.*?[^\.]',
  51. '#': r'.*?'}
  52. #: compiled regex cache
  53. _compiled = {}
  54. def lookup(self, table, exchange, routing_key, default):
  55. return [queue for rkey, pattern, queue in table
  56. if self._match(pattern, routing_key)]
  57. def deliver(self, message, exchange, routing_key, **kwargs):
  58. _lookup = self.channel._lookup
  59. _put = self.channel._put
  60. deadletter = self.channel.deadletter_queue
  61. for queue in [q for q in _lookup(exchange, routing_key)
  62. if q and q != deadletter]:
  63. _put(queue, message, **kwargs)
  64. def prepare_bind(self, queue, exchange, routing_key, arguments):
  65. return routing_key, self.key_to_pattern(routing_key), queue
  66. def key_to_pattern(self, rkey):
  67. """Get the corresponding regex for any routing key."""
  68. return '^%s$' % ('\.'.join(
  69. self.wildcards.get(word, word)
  70. for word in escape_regex(rkey, '.#*').split('.')
  71. ))
  72. def _match(self, pattern, string):
  73. """Same as :func:`re.match`, except the regex is compiled and cached,
  74. then reused on subsequent matches with the same pattern."""
  75. try:
  76. compiled = self._compiled[pattern]
  77. except KeyError:
  78. compiled = self._compiled[pattern] = re.compile(pattern, re.U)
  79. return compiled.match(string)
  80. class FanoutExchange(ExchangeType):
  81. """The `fanout` exchange implements broadcast messaging by delivering
  82. copies of all messages to all queues bound to the exchange.
  83. To support fanout the virtual channel needs to store the table
  84. as shared state. This requires that the `Channel.supports_fanout`
  85. attribute is set to true, and the `Channel._queue_bind` and
  86. `Channel.get_table` methods are implemented. See the redis backend
  87. for an example implementation of these methods.
  88. """
  89. type = 'fanout'
  90. def lookup(self, table, exchange, routing_key, default):
  91. return [queue for _, _, queue in table]
  92. def deliver(self, message, exchange, routing_key, **kwargs):
  93. if self.channel.supports_fanout:
  94. self.channel._put_fanout(
  95. exchange, message, routing_key, **kwargs)
  96. #: Map of standard exchange types and corresponding classes.
  97. STANDARD_EXCHANGE_TYPES = {'direct': DirectExchange,
  98. 'topic': TopicExchange,
  99. 'fanout': FanoutExchange}