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.

__init__.py 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. """Kombu transport using SQLAlchemy as the message store."""
  2. # SQLAlchemy overrides != False to have special meaning and pep8 complains
  3. # flake8: noqa
  4. from __future__ import absolute_import
  5. from anyjson import loads, dumps
  6. from sqlalchemy import create_engine
  7. from sqlalchemy.exc import OperationalError
  8. from sqlalchemy.orm import sessionmaker
  9. from kombu.five import Empty
  10. from kombu.transport import virtual
  11. from kombu.utils import cached_property
  12. from kombu.utils.encoding import bytes_to_str
  13. from .models import (ModelBase, Queue as QueueBase, Message as MessageBase,
  14. class_registry, metadata)
  15. VERSION = (1, 1, 0)
  16. __version__ = '.'.join(map(str, VERSION))
  17. class Channel(virtual.Channel):
  18. _session = None
  19. _engines = {} # engine cache
  20. def __init__(self, connection, **kwargs):
  21. self._configure_entity_tablenames(connection.client.transport_options)
  22. super(Channel, self).__init__(connection, **kwargs)
  23. def _configure_entity_tablenames(self, opts):
  24. self.queue_tablename = opts.get('queue_tablename', 'kombu_queue')
  25. self.message_tablename = opts.get('message_tablename', 'kombu_message')
  26. #
  27. # Define the model definitions. This registers the declarative
  28. # classes with the active SQLAlchemy metadata object. This *must* be
  29. # done prior to the ``create_engine`` call.
  30. #
  31. self.queue_cls and self.message_cls
  32. def _engine_from_config(self):
  33. conninfo = self.connection.client
  34. transport_options = conninfo.transport_options.copy()
  35. transport_options.pop('queue_tablename', None)
  36. transport_options.pop('message_tablename', None)
  37. return create_engine(conninfo.hostname, **transport_options)
  38. def _open(self):
  39. conninfo = self.connection.client
  40. if conninfo.hostname not in self._engines:
  41. engine = self._engine_from_config()
  42. Session = sessionmaker(bind=engine)
  43. metadata.create_all(engine)
  44. self._engines[conninfo.hostname] = engine, Session
  45. return self._engines[conninfo.hostname]
  46. @property
  47. def session(self):
  48. if self._session is None:
  49. _, Session = self._open()
  50. self._session = Session()
  51. return self._session
  52. def _get_or_create(self, queue):
  53. obj = self.session.query(self.queue_cls) \
  54. .filter(self.queue_cls.name == queue).first()
  55. if not obj:
  56. obj = self.queue_cls(queue)
  57. self.session.add(obj)
  58. try:
  59. self.session.commit()
  60. except OperationalError:
  61. self.session.rollback()
  62. return obj
  63. def _new_queue(self, queue, **kwargs):
  64. self._get_or_create(queue)
  65. def _put(self, queue, payload, **kwargs):
  66. obj = self._get_or_create(queue)
  67. message = self.message_cls(dumps(payload), obj)
  68. self.session.add(message)
  69. try:
  70. self.session.commit()
  71. except OperationalError:
  72. self.session.rollback()
  73. def _get(self, queue):
  74. obj = self._get_or_create(queue)
  75. if self.session.bind.name == 'sqlite':
  76. self.session.execute('BEGIN IMMEDIATE TRANSACTION')
  77. try:
  78. msg = self.session.query(self.message_cls) \
  79. .with_lockmode('update') \
  80. .filter(self.message_cls.queue_id == obj.id) \
  81. .filter(self.message_cls.visible != False) \
  82. .order_by(self.message_cls.sent_at) \
  83. .order_by(self.message_cls.id) \
  84. .limit(1) \
  85. .first()
  86. if msg:
  87. msg.visible = False
  88. return loads(bytes_to_str(msg.payload))
  89. raise Empty()
  90. finally:
  91. self.session.commit()
  92. def _query_all(self, queue):
  93. obj = self._get_or_create(queue)
  94. return self.session.query(self.message_cls) \
  95. .filter(self.message_cls.queue_id == obj.id)
  96. def _purge(self, queue):
  97. count = self._query_all(queue).delete(synchronize_session=False)
  98. try:
  99. self.session.commit()
  100. except OperationalError:
  101. self.session.rollback()
  102. return count
  103. def _size(self, queue):
  104. return self._query_all(queue).count()
  105. def _declarative_cls(self, name, base, ns):
  106. if name in class_registry:
  107. return class_registry[name]
  108. return type(name, (base, ModelBase), ns)
  109. @cached_property
  110. def queue_cls(self):
  111. return self._declarative_cls(
  112. 'Queue',
  113. QueueBase,
  114. {'__tablename__': self.queue_tablename}
  115. )
  116. @cached_property
  117. def message_cls(self):
  118. return self._declarative_cls(
  119. 'Message',
  120. MessageBase,
  121. {'__tablename__': self.message_tablename}
  122. )
  123. class Transport(virtual.Transport):
  124. Channel = Channel
  125. can_parse_url = True
  126. default_port = 0
  127. driver_type = 'sql'
  128. driver_name = 'sqlalchemy'
  129. connection_errors = (OperationalError, )
  130. def driver_version(self):
  131. import sqlalchemy
  132. return sqlalchemy.__version__