Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1277)

Side by Side Diff: reviewbot/third_party/google-api-python-client/oauth2client/locked_file.py

Issue 20515002: Add google-api-python-client in third_party/ (Closed) Base URL: https://src.chromium.org/chrome/trunk/tools/
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 # Copyright 2011 Google Inc. All Rights Reserved.
2
3 """Locked file interface that should work on Unix and Windows pythons.
4
5 This module first tries to use fcntl locking to ensure serialized access
6 to a file, then falls back on a lock file if that is unavialable.
7
8 Usage:
9 f = LockedFile('filename', 'r+b', 'rb')
10 f.open_and_lock()
11 if f.is_locked():
12 print 'Acquired filename with r+b mode'
13 f.file_handle().write('locked data')
14 else:
15 print 'Aquired filename with rb mode'
16 f.unlock_and_close()
17 """
18
19 __author__ = 'cache@google.com (David T McWherter)'
20
21 import errno
22 import logging
23 import os
24 import time
25
26 from oauth2client import util
27
28 logger = logging.getLogger(__name__)
29
30
31 class CredentialsFileSymbolicLinkError(Exception):
32 """Credentials files must not be symbolic links."""
33
34
35 class AlreadyLockedException(Exception):
36 """Trying to lock a file that has already been locked by the LockedFile."""
37 pass
38
39
40 def validate_file(filename):
41 if os.path.islink(filename):
42 raise CredentialsFileSymbolicLinkError(
43 'File: %s is a symbolic link.' % filename)
44
45 class _Opener(object):
46 """Base class for different locking primitives."""
47
48 def __init__(self, filename, mode, fallback_mode):
49 """Create an Opener.
50
51 Args:
52 filename: string, The pathname of the file.
53 mode: string, The preferred mode to access the file with.
54 fallback_mode: string, The mode to use if locking fails.
55 """
56 self._locked = False
57 self._filename = filename
58 self._mode = mode
59 self._fallback_mode = fallback_mode
60 self._fh = None
61
62 def is_locked(self):
63 """Was the file locked."""
64 return self._locked
65
66 def file_handle(self):
67 """The file handle to the file. Valid only after opened."""
68 return self._fh
69
70 def filename(self):
71 """The filename that is being locked."""
72 return self._filename
73
74 def open_and_lock(self, timeout, delay):
75 """Open the file and lock it.
76
77 Args:
78 timeout: float, How long to try to lock for.
79 delay: float, How long to wait between retries.
80 """
81 pass
82
83 def unlock_and_close(self):
84 """Unlock and close the file."""
85 pass
86
87
88 class _PosixOpener(_Opener):
89 """Lock files using Posix advisory lock files."""
90
91 def open_and_lock(self, timeout, delay):
92 """Open the file and lock it.
93
94 Tries to create a .lock file next to the file we're trying to open.
95
96 Args:
97 timeout: float, How long to try to lock for.
98 delay: float, How long to wait between retries.
99
100 Raises:
101 AlreadyLockedException: if the lock is already acquired.
102 IOError: if the open fails.
103 CredentialsFileSymbolicLinkError if the file is a symbolic link.
104 """
105 if self._locked:
106 raise AlreadyLockedException('File %s is already locked' %
107 self._filename)
108 self._locked = False
109
110 validate_file(self._filename)
111 try:
112 self._fh = open(self._filename, self._mode)
113 except IOError, e:
114 # If we can't access with _mode, try _fallback_mode and don't lock.
115 if e.errno == errno.EACCES:
116 self._fh = open(self._filename, self._fallback_mode)
117 return
118
119 lock_filename = self._posix_lockfile(self._filename)
120 start_time = time.time()
121 while True:
122 try:
123 self._lock_fd = os.open(lock_filename,
124 os.O_CREAT|os.O_EXCL|os.O_RDWR)
125 self._locked = True
126 break
127
128 except OSError, e:
129 if e.errno != errno.EEXIST:
130 raise
131 if (time.time() - start_time) >= timeout:
132 logger.warn('Could not acquire lock %s in %s seconds' % (
133 lock_filename, timeout))
134 # Close the file and open in fallback_mode.
135 if self._fh:
136 self._fh.close()
137 self._fh = open(self._filename, self._fallback_mode)
138 return
139 time.sleep(delay)
140
141 def unlock_and_close(self):
142 """Unlock a file by removing the .lock file, and close the handle."""
143 if self._locked:
144 lock_filename = self._posix_lockfile(self._filename)
145 os.close(self._lock_fd)
146 os.unlink(lock_filename)
147 self._locked = False
148 self._lock_fd = None
149 if self._fh:
150 self._fh.close()
151
152 def _posix_lockfile(self, filename):
153 """The name of the lock file to use for posix locking."""
154 return '%s.lock' % filename
155
156
157 try:
158 import fcntl
159
160 class _FcntlOpener(_Opener):
161 """Open, lock, and unlock a file using fcntl.lockf."""
162
163 def open_and_lock(self, timeout, delay):
164 """Open the file and lock it.
165
166 Args:
167 timeout: float, How long to try to lock for.
168 delay: float, How long to wait between retries
169
170 Raises:
171 AlreadyLockedException: if the lock is already acquired.
172 IOError: if the open fails.
173 CredentialsFileSymbolicLinkError if the file is a symbolic link.
174 """
175 if self._locked:
176 raise AlreadyLockedException('File %s is already locked' %
177 self._filename)
178 start_time = time.time()
179
180 validate_file(self._filename)
181 try:
182 self._fh = open(self._filename, self._mode)
183 except IOError, e:
184 # If we can't access with _mode, try _fallback_mode and don't lock.
185 if e.errno == errno.EACCES:
186 self._fh = open(self._filename, self._fallback_mode)
187 return
188
189 # We opened in _mode, try to lock the file.
190 while True:
191 try:
192 fcntl.lockf(self._fh.fileno(), fcntl.LOCK_EX)
193 self._locked = True
194 return
195 except IOError, e:
196 # If not retrying, then just pass on the error.
197 if timeout == 0:
198 raise e
199 if e.errno != errno.EACCES:
200 raise e
201 # We could not acquire the lock. Try again.
202 if (time.time() - start_time) >= timeout:
203 logger.warn('Could not lock %s in %s seconds' % (
204 self._filename, timeout))
205 if self._fh:
206 self._fh.close()
207 self._fh = open(self._filename, self._fallback_mode)
208 return
209 time.sleep(delay)
210
211 def unlock_and_close(self):
212 """Close and unlock the file using the fcntl.lockf primitive."""
213 if self._locked:
214 fcntl.lockf(self._fh.fileno(), fcntl.LOCK_UN)
215 self._locked = False
216 if self._fh:
217 self._fh.close()
218 except ImportError:
219 _FcntlOpener = None
220
221
222 try:
223 import pywintypes
224 import win32con
225 import win32file
226
227 class _Win32Opener(_Opener):
228 """Open, lock, and unlock a file using windows primitives."""
229
230 # Error #33:
231 # 'The process cannot access the file because another process'
232 FILE_IN_USE_ERROR = 33
233
234 # Error #158:
235 # 'The segment is already unlocked.'
236 FILE_ALREADY_UNLOCKED_ERROR = 158
237
238 def open_and_lock(self, timeout, delay):
239 """Open the file and lock it.
240
241 Args:
242 timeout: float, How long to try to lock for.
243 delay: float, How long to wait between retries
244
245 Raises:
246 AlreadyLockedException: if the lock is already acquired.
247 IOError: if the open fails.
248 CredentialsFileSymbolicLinkError if the file is a symbolic link.
249 """
250 if self._locked:
251 raise AlreadyLockedException('File %s is already locked' %
252 self._filename)
253 start_time = time.time()
254
255 validate_file(self._filename)
256 try:
257 self._fh = open(self._filename, self._mode)
258 except IOError, e:
259 # If we can't access with _mode, try _fallback_mode and don't lock.
260 if e.errno == errno.EACCES:
261 self._fh = open(self._filename, self._fallback_mode)
262 return
263
264 # We opened in _mode, try to lock the file.
265 while True:
266 try:
267 hfile = win32file._get_osfhandle(self._fh.fileno())
268 win32file.LockFileEx(
269 hfile,
270 (win32con.LOCKFILE_FAIL_IMMEDIATELY|
271 win32con.LOCKFILE_EXCLUSIVE_LOCK), 0, -0x10000,
272 pywintypes.OVERLAPPED())
273 self._locked = True
274 return
275 except pywintypes.error, e:
276 if timeout == 0:
277 raise e
278
279 # If the error is not that the file is already in use, raise.
280 if e[0] != _Win32Opener.FILE_IN_USE_ERROR:
281 raise
282
283 # We could not acquire the lock. Try again.
284 if (time.time() - start_time) >= timeout:
285 logger.warn('Could not lock %s in %s seconds' % (
286 self._filename, timeout))
287 if self._fh:
288 self._fh.close()
289 self._fh = open(self._filename, self._fallback_mode)
290 return
291 time.sleep(delay)
292
293 def unlock_and_close(self):
294 """Close and unlock the file using the win32 primitive."""
295 if self._locked:
296 try:
297 hfile = win32file._get_osfhandle(self._fh.fileno())
298 win32file.UnlockFileEx(hfile, 0, -0x10000, pywintypes.OVERLAPPED())
299 except pywintypes.error, e:
300 if e[0] != _Win32Opener.FILE_ALREADY_UNLOCKED_ERROR:
301 raise
302 self._locked = False
303 if self._fh:
304 self._fh.close()
305 except ImportError:
306 _Win32Opener = None
307
308
309 class LockedFile(object):
310 """Represent a file that has exclusive access."""
311
312 @util.positional(4)
313 def __init__(self, filename, mode, fallback_mode, use_native_locking=True):
314 """Construct a LockedFile.
315
316 Args:
317 filename: string, The path of the file to open.
318 mode: string, The mode to try to open the file with.
319 fallback_mode: string, The mode to use if locking fails.
320 use_native_locking: bool, Whether or not fcntl/win32 locking is used.
321 """
322 opener = None
323 if not opener and use_native_locking:
324 if _Win32Opener:
325 opener = _Win32Opener(filename, mode, fallback_mode)
326 if _FcntlOpener:
327 opener = _FcntlOpener(filename, mode, fallback_mode)
328
329 if not opener:
330 opener = _PosixOpener(filename, mode, fallback_mode)
331
332 self._opener = opener
333
334 def filename(self):
335 """Return the filename we were constructed with."""
336 return self._opener._filename
337
338 def file_handle(self):
339 """Return the file_handle to the opened file."""
340 return self._opener.file_handle()
341
342 def is_locked(self):
343 """Return whether we successfully locked the file."""
344 return self._opener.is_locked()
345
346 def open_and_lock(self, timeout=0, delay=0.05):
347 """Open the file, trying to lock it.
348
349 Args:
350 timeout: float, The number of seconds to try to acquire the lock.
351 delay: float, The number of seconds to wait between retry attempts.
352
353 Raises:
354 AlreadyLockedException: if the lock is already acquired.
355 IOError: if the open fails.
356 """
357 self._opener.open_and_lock(timeout, delay)
358
359 def unlock_and_close(self):
360 """Unlock and close a file."""
361 self._opener.unlock_and_close()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698