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