__init__.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import errno
  2. import logging
  3. import os
  4. import sys
  5. import library.python.windows
  6. logger = logging.getLogger(__name__)
  7. def set_close_on_exec(stream):
  8. if library.python.windows.on_win():
  9. library.python.windows.set_handle_information(stream, inherit=False)
  10. else:
  11. import fcntl
  12. fcntl.fcntl(stream, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
  13. class AbstractFileLock(object):
  14. def __init__(self, path):
  15. self.path = path
  16. def acquire(self, blocking=True):
  17. raise NotImplementedError
  18. def release(self):
  19. raise NotImplementedError
  20. def __enter__(self):
  21. self.acquire()
  22. return self
  23. def __exit__(self, type, value, traceback):
  24. self.release()
  25. class _NixFileLock(AbstractFileLock):
  26. def __init__(self, path):
  27. super(_NixFileLock, self).__init__(path)
  28. from fcntl import flock, LOCK_EX, LOCK_UN, LOCK_NB
  29. self._locker = lambda lock, blocking: flock(lock, LOCK_EX if blocking else LOCK_EX | LOCK_NB)
  30. self._unlocker = lambda lock: flock(lock, LOCK_UN)
  31. self._lock = open(self.path, 'a')
  32. set_close_on_exec(self._lock)
  33. def acquire(self, blocking=True):
  34. import errno
  35. try:
  36. self._locker(self._lock, blocking)
  37. except IOError as e:
  38. if e.errno in (errno.EAGAIN, errno.EACCES) and not blocking:
  39. return False
  40. raise
  41. return True
  42. def release(self):
  43. self._unlocker(self._lock)
  44. def __del__(self):
  45. if hasattr(self, "_lock"):
  46. self._lock.close()
  47. class _WinFileLock(AbstractFileLock):
  48. """
  49. Based on LockFile / UnlockFile from win32 API
  50. https://msdn.microsoft.com/en-us/library/windows/desktop/aa365202(v=vs.85).aspx
  51. """
  52. _LOCKED_BYTES_NUM = 1
  53. def __init__(self, path):
  54. super(_WinFileLock, self).__init__(path)
  55. self._lock = None
  56. try:
  57. with open(path, 'w') as lock_file:
  58. lock_file.write(" " * self._LOCKED_BYTES_NUM)
  59. except IOError as e:
  60. if e.errno != errno.EACCES or not os.path.isfile(path):
  61. raise
  62. def acquire(self, blocking=True):
  63. self._lock = open(self.path)
  64. set_close_on_exec(self._lock)
  65. import time
  66. locked = False
  67. while not locked:
  68. locked = library.python.windows.lock_file(self._lock, 0, self._LOCKED_BYTES_NUM, raises=False)
  69. if locked:
  70. return True
  71. if blocking:
  72. time.sleep(.5)
  73. else:
  74. return False
  75. def release(self):
  76. if self._lock:
  77. library.python.windows.unlock_file(self._lock, 0, self._LOCKED_BYTES_NUM, raises=False)
  78. self._lock.close()
  79. self._lock = None
  80. class FileLock(AbstractFileLock):
  81. def __init__(self, path):
  82. super(FileLock, self).__init__(path)
  83. if sys.platform.startswith('win'):
  84. self._lock = _WinFileLock(path)
  85. else:
  86. self._lock = _NixFileLock(path)
  87. def acquire(self, blocking=True):
  88. logger.debug('Acquiring filelock (blocking=%s): %s', blocking, self.path)
  89. return self._lock.acquire(blocking)
  90. def release(self):
  91. logger.debug('Ensuring filelock released: %s', self.path)
  92. return self._lock.release()