1
2
3 """
4 File Locking
5 ============
6
7 This module provides portable advisory file locking primitives that operate on
8 file descriptors. POSIX-like systems and Windows systems use different
9 primitives to perform file locking, and these different primitives are modeled
10 by incompatible (and different) modules in the Python standard library. This
11 module provides an abstract ``FileLock`` class, and underlying
12 implementations, to hide the operating system dependencies behind a simple
13 portable interface.
14
15 To create a file lock, simply instantiate the ``FileLock`` class with an open
16 file descriptor. It handles the rest:
17
18 .. python::
19
20 from grizzled.io.filelock import FileLock
21
22 fd = open('/tmp/lockfile', 'r+')
23 lock = FileLock(fd)
24 lock.acquire()
25
26 ...
27
28 lock.release()
29
30 You can also use the ``locked_file()`` function to simplify your code:
31
32 .. python::
33
34 from grizzled.io.filelock import locked_file
35
36 fd = open('/tmp/lockfile', 'r+')
37 with locked_file(fd):
38 ...
39 """
40
41 __docformat__ = "restructuredtext en"
42
43
44
45
46
47 import os
48 from contextlib import contextmanager
49
50
51
52
53
54 __all__ = ['FileLock', 'locked_file']
55
56 LOCK_CLASSES = {'posix' : '_PosixFileLock',
57 'nt' : '_WindowsFileLock'}
64 """
65 A ``FileLock`` object models a file lock. It wraps a file descriptor
66 and contains methods to acquire and release a lock on the file.
67
68 File lock implementations that implement this interface are guaranteed
69 to be advisory, but not mandatory, file locks. (They may, in fact, also
70 be mandatory file locks, but they are not guaranteed to be.)
71
72 Currently, there are underlying implementations for both POSIX systems
73 and Windows.
74 """
75
77 """
78 Allocate a new file lock that operates on the specified file
79 descriptor.
80
81 :Parameters:
82 fd : int
83 Open file descriptor. The file must be opened for writing or
84 updating, not reading.
85 """
86 try:
87 cls = eval(LOCK_CLASSES[os.name])
88 self.lock = cls(fd)
89
90 except KeyError:
91 raise NotImplementedError, \
92 '''Don't know how to lock files on "%s" systems.''' % os.name
93
95 """
96 Lock the associated file. If someone already has the file locked,
97 this method will suspend the calling process, unless ``no_wait`` is
98 ``True``.
99
100 :Parameters:
101 no_wait : bool
102 If ``False``, then ``acquire()`` will suspend the calling
103 process if someone has the file locked. If ``True``, then
104 ``acquire()`` will raise an ``IOError`` if the file is
105 locked by someone else.
106
107 :raise IOError: If the file cannot be locked for any reason.
108 """
109 self.lock.acquire(no_wait)
110
112 """
113 Unlock (i.e., release the lock on) the associated file.
114 """
115 self.lock.release()
116
118 """File lock implementation for POSIX-compliant systems."""
119
122
124 import fcntl
125 flags = fcntl.LOCK_EX
126 if no_wait:
127 flags |= fcntl.LOCK_NB
128
129 fcntl.lockf(self.fd, flags)
130
132 import fcntl
133 fcntl.lockf(self.fd, fcntl.LOCK_UN)
134
136 """File lock implementation for Windows systems."""
137
140
141 - def lock(self, no_wait=False):
142 import msvcrt
143 if no_wait:
144 op = msvcrt.LK_NBLCK
145 else:
146 op = msvcrt.LK_LOCK
147
148 self.fd.seek(0)
149 msvcrt.locking(self.fd, op, 1)
150
152 import msvcrt
153 self.fd.seek(0)
154 msvcrt.locking(self.fd, LK_UNLCK, 1)
155
156
157
158
159
160 @contextmanager
161 -def locked_file(fd, no_wait=False):
162 """
163 This function is intended to be used as a ``with`` statement context
164 manager. It wraps a ``FileLock`` object so that the locking and unlocking
165 of the file descriptor are automatic. With the ``locked_file()`` function,
166 you can replace this code:
167
168 .. python::
169
170 lock = FileLock(fd)
171 lock.acquire()
172 try:
173 do_something()
174 finally:
175 lock.release()
176
177 with this code:
178
179 .. python::
180
181 with locked_file(fd):
182 do_something()
183
184 :Parameters:
185 fd : int
186 Open file descriptor. The file must be opened for writing
187 or updating, not reading.
188 no_wait : bool
189 If ``False``, then ``locked_file()`` will suspend the calling
190 process if someone has the file locked. If ``True``, then
191 ``locked_file()`` will raise an ``IOError`` if the file is
192 locked by someone else.
193 """
194 locked = False
195 try:
196 lock = FileLock(fd)
197 lock.acquire(no_wait)
198 locked = True
199 yield lock
200 finally:
201 if locked:
202 lock.release()
203