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.

filesystem.py 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. """
  2. kombu.transport.filesystem
  3. ==========================
  4. Transport using the file system as the message store.
  5. """
  6. from __future__ import absolute_import
  7. from anyjson import loads, dumps
  8. import os
  9. import shutil
  10. import uuid
  11. import tempfile
  12. from . import virtual
  13. from kombu.exceptions import ChannelError
  14. from kombu.five import Empty, monotonic
  15. from kombu.utils import cached_property
  16. from kombu.utils.encoding import bytes_to_str, str_to_bytes
  17. VERSION = (1, 0, 0)
  18. __version__ = '.'.join(map(str, VERSION))
  19. # needs win32all to work on Windows
  20. if os.name == 'nt':
  21. import win32con
  22. import win32file
  23. import pywintypes
  24. LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
  25. # 0 is the default
  26. LOCK_SH = 0 # noqa
  27. LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY # noqa
  28. __overlapped = pywintypes.OVERLAPPED()
  29. def lock(file, flags):
  30. hfile = win32file._get_osfhandle(file.fileno())
  31. win32file.LockFileEx(hfile, flags, 0, 0xffff0000, __overlapped)
  32. def unlock(file):
  33. hfile = win32file._get_osfhandle(file.fileno())
  34. win32file.UnlockFileEx(hfile, 0, 0xffff0000, __overlapped)
  35. elif os.name == 'posix':
  36. import fcntl
  37. from fcntl import LOCK_EX, LOCK_SH, LOCK_NB # noqa
  38. def lock(file, flags): # noqa
  39. fcntl.flock(file.fileno(), flags)
  40. def unlock(file): # noqa
  41. fcntl.flock(file.fileno(), fcntl.LOCK_UN)
  42. else:
  43. raise RuntimeError(
  44. 'Filesystem plugin only defined for NT and POSIX platforms')
  45. class Channel(virtual.Channel):
  46. def _put(self, queue, payload, **kwargs):
  47. """Put `message` onto `queue`."""
  48. filename = '%s_%s.%s.msg' % (int(round(monotonic() * 1000)),
  49. uuid.uuid4(), queue)
  50. filename = os.path.join(self.data_folder_out, filename)
  51. try:
  52. f = open(filename, 'wb')
  53. lock(f, LOCK_EX)
  54. f.write(str_to_bytes(dumps(payload)))
  55. except (IOError, OSError):
  56. raise ChannelError(
  57. 'Cannot add file {0!r} to directory'.format(filename))
  58. finally:
  59. unlock(f)
  60. f.close()
  61. def _get(self, queue):
  62. """Get next message from `queue`."""
  63. queue_find = '.' + queue + '.msg'
  64. folder = os.listdir(self.data_folder_in)
  65. folder = sorted(folder)
  66. while len(folder) > 0:
  67. filename = folder.pop(0)
  68. # only handle message for the requested queue
  69. if filename.find(queue_find) < 0:
  70. continue
  71. if self.store_processed:
  72. processed_folder = self.processed_folder
  73. else:
  74. processed_folder = tempfile.gettempdir()
  75. try:
  76. # move the file to the tmp/processed folder
  77. shutil.move(os.path.join(self.data_folder_in, filename),
  78. processed_folder)
  79. except IOError:
  80. pass # file could be locked, or removed in meantime so ignore
  81. filename = os.path.join(processed_folder, filename)
  82. try:
  83. f = open(filename, 'rb')
  84. payload = f.read()
  85. f.close()
  86. if not self.store_processed:
  87. os.remove(filename)
  88. except (IOError, OSError):
  89. raise ChannelError(
  90. 'Cannot read file {0!r} from queue.'.format(filename))
  91. return loads(bytes_to_str(payload))
  92. raise Empty()
  93. def _purge(self, queue):
  94. """Remove all messages from `queue`."""
  95. count = 0
  96. queue_find = '.' + queue + '.msg'
  97. folder = os.listdir(self.data_folder_in)
  98. while len(folder) > 0:
  99. filename = folder.pop()
  100. try:
  101. # only purge messages for the requested queue
  102. if filename.find(queue_find) < 0:
  103. continue
  104. filename = os.path.join(self.data_folder_in, filename)
  105. os.remove(filename)
  106. count += 1
  107. except OSError:
  108. # we simply ignore its existence, as it was probably
  109. # processed by another worker
  110. pass
  111. return count
  112. def _size(self, queue):
  113. """Return the number of messages in `queue` as an :class:`int`."""
  114. count = 0
  115. queue_find = '.{0}.msg'.format(queue)
  116. folder = os.listdir(self.data_folder_in)
  117. while len(folder) > 0:
  118. filename = folder.pop()
  119. # only handle message for the requested queue
  120. if filename.find(queue_find) < 0:
  121. continue
  122. count += 1
  123. return count
  124. @property
  125. def transport_options(self):
  126. return self.connection.client.transport_options
  127. @cached_property
  128. def data_folder_in(self):
  129. return self.transport_options.get('data_folder_in', 'data_in')
  130. @cached_property
  131. def data_folder_out(self):
  132. return self.transport_options.get('data_folder_out', 'data_out')
  133. @cached_property
  134. def store_processed(self):
  135. return self.transport_options.get('store_processed', False)
  136. @cached_property
  137. def processed_folder(self):
  138. return self.transport_options.get('processed_folder', 'processed')
  139. class Transport(virtual.Transport):
  140. Channel = Channel
  141. default_port = 0
  142. driver_type = 'filesystem'
  143. driver_name = 'filesystem'
  144. def driver_version(self):
  145. return 'N/A'