Package cherrypy :: Package lib :: Module lockfile
[hide private]
[frames] | no frames]

Source Code for Module cherrypy.lib.lockfile

  1  """
 
  2  Platform-independent file locking. Inspired by and modeled after zc.lockfile.
 
  3  """ 
  4  
 
  5  import os 
  6  
 
  7  try: 
  8      import msvcrt 
  9  except ImportError: 
 10      pass 
 11  
 
 12  try: 
 13      import fcntl 
 14  except ImportError: 
 15      pass 
 16  
 
 17  
 
18 -class LockError(Exception):
19 20 "Could not obtain a lock" 21 22 msg = "Unable to lock %r" 23
24 - def __init__(self, path):
25 super(LockError, self).__init__(self.msg % path)
26 27
28 -class UnlockError(LockError):
29 30 "Could not release a lock" 31 32 msg = "Unable to unlock %r"
33 34 35 # first, a default, naive locking implementation
36 -class LockFile(object):
37 38 """ 39 A default, naive locking implementation. Always fails if the file 40 already exists. 41 """ 42
43 - def __init__(self, path):
44 self.path = path 45 try: 46 fd = os.open(path, os.O_CREAT | os.O_WRONLY | os.O_EXCL) 47 except OSError: 48 raise LockError(self.path) 49 os.close(fd)
50
51 - def release(self):
52 os.remove(self.path)
53
54 - def remove(self):
55 pass
56 57
58 -class SystemLockFile(object):
59 60 """ 61 An abstract base class for platform-specific locking. 62 """ 63
64 - def __init__(self, path):
65 self.path = path 66 67 try: 68 # Open lockfile for writing without truncation: 69 self.fp = open(path, 'r+') 70 except IOError: 71 # If the file doesn't exist, IOError is raised; Use a+ instead. 72 # Note that there may be a race here. Multiple processes 73 # could fail on the r+ open and open the file a+, but only 74 # one will get the the lock and write a pid. 75 self.fp = open(path, 'a+') 76 77 try: 78 self._lock_file() 79 except: 80 self.fp.seek(1) 81 self.fp.close() 82 del self.fp 83 raise 84 85 self.fp.write(" %s\n" % os.getpid()) 86 self.fp.truncate() 87 self.fp.flush()
88
89 - def release(self):
90 if not hasattr(self, 'fp'): 91 return 92 self._unlock_file() 93 self.fp.close() 94 del self.fp
95
96 - def remove(self):
97 """ 98 Attempt to remove the file 99 """ 100 try: 101 os.remove(self.path) 102 except: 103 pass
104 105 #@abc.abstract_method 106 # def _lock_file(self): 107 # """Attempt to obtain the lock on self.fp. Raise LockError if not 108 # acquired.""" 109
110 - def _unlock_file(self):
111 """Attempt to obtain the lock on self.fp. Raise UnlockError if not 112 released."""
113 114
115 -class WindowsLockFile(SystemLockFile):
116
117 - def _lock_file(self):
118 # Lock just the first byte 119 try: 120 msvcrt.locking(self.fp.fileno(), msvcrt.LK_NBLCK, 1) 121 except IOError: 122 raise LockError(self.fp.name)
123
124 - def _unlock_file(self):
125 try: 126 self.fp.seek(0) 127 msvcrt.locking(self.fp.fileno(), msvcrt.LK_UNLCK, 1) 128 except IOError: 129 raise UnlockError(self.fp.name)
130 131 if 'msvcrt' in globals(): 132 LockFile = WindowsLockFile 133 134
135 -class UnixLockFile(SystemLockFile):
136
137 - def _lock_file(self):
138 flags = fcntl.LOCK_EX | fcntl.LOCK_NB 139 try: 140 fcntl.flock(self.fp.fileno(), flags) 141 except IOError: 142 raise LockError(self.fp.name)
143 144 # no need to implement _unlock_file, it will be unlocked on close() 145 146 if 'fcntl' in globals(): 147 LockFile = UnixLockFile 148