_unix.py 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. from __future__ import annotations
  2. import os
  3. import sys
  4. from contextlib import suppress
  5. from errno import ENOSYS
  6. from pathlib import Path
  7. from typing import cast
  8. from ._api import BaseFileLock
  9. from ._util import ensure_directory_exists
  10. #: a flag to indicate if the fcntl API is available
  11. has_fcntl = False
  12. if sys.platform == "win32": # pragma: win32 cover
  13. class UnixFileLock(BaseFileLock):
  14. """Uses the :func:`fcntl.flock` to hard lock the lock file on unix systems."""
  15. def _acquire(self) -> None:
  16. raise NotImplementedError
  17. def _release(self) -> None:
  18. raise NotImplementedError
  19. else: # pragma: win32 no cover
  20. try:
  21. import fcntl
  22. except ImportError:
  23. pass
  24. else:
  25. has_fcntl = True
  26. class UnixFileLock(BaseFileLock):
  27. """Uses the :func:`fcntl.flock` to hard lock the lock file on unix systems."""
  28. def _acquire(self) -> None:
  29. ensure_directory_exists(self.lock_file)
  30. open_flags = os.O_RDWR | os.O_TRUNC
  31. if not Path(self.lock_file).exists():
  32. open_flags |= os.O_CREAT
  33. fd = os.open(self.lock_file, open_flags, self._context.mode)
  34. with suppress(PermissionError): # This locked is not owned by this UID
  35. os.fchmod(fd, self._context.mode)
  36. try:
  37. fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
  38. except OSError as exception:
  39. os.close(fd)
  40. if exception.errno == ENOSYS: # NotImplemented error
  41. msg = "FileSystem does not appear to support flock; use SoftFileLock instead"
  42. raise NotImplementedError(msg) from exception
  43. else:
  44. self._context.lock_file_fd = fd
  45. def _release(self) -> None:
  46. # Do not remove the lockfile:
  47. # https://github.com/tox-dev/py-filelock/issues/31
  48. # https://stackoverflow.com/questions/17708885/flock-removing-locked-file-without-race-condition
  49. fd = cast(int, self._context.lock_file_fd)
  50. self._context.lock_file_fd = None
  51. fcntl.flock(fd, fcntl.LOCK_UN)
  52. os.close(fd)
  53. __all__ = [
  54. "UnixFileLock",
  55. "has_fcntl",
  56. ]