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

Side by Side Diff: third_party/google-endpoints/oauth2client/locked_file.py

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

Powered by Google App Engine
This is Rietveld 408576698