| Viewing file:  __init__.py (9.15 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
# -*- coding: utf-8 -*-
 """
 lockfile.py - Platform-independent advisory file locks.
 
 Requires Python 2.5 unless you apply 2.4.diff
 Locking is done on a per-thread basis instead of a per-process basis.
 
 Usage:
 
 >>> lock = LockFile('somefile')
 >>> try:
 ...     lock.acquire()
 ... except AlreadyLocked:
 ...     print 'somefile', 'is locked already.'
 ... except LockFailed:
 ...     print 'somefile', 'can\\'t be locked.'
 ... else:
 ...     print 'got lock'
 got lock
 >>> print lock.is_locked()
 True
 >>> lock.release()
 
 >>> lock = LockFile('somefile')
 >>> print lock.is_locked()
 False
 >>> with lock:
 ...    print lock.is_locked()
 True
 >>> print lock.is_locked()
 False
 
 >>> lock = LockFile('somefile')
 >>> # It is okay to lock twice from the same thread...
 >>> with lock:
 ...     lock.acquire()
 ...
 >>> # Though no counter is kept, so you can't unlock multiple times...
 >>> print lock.is_locked()
 False
 
 Exceptions:
 
 Error - base class for other exceptions
 LockError - base class for all locking exceptions
 AlreadyLocked - Another thread or process already holds the lock
 LockFailed - Lock failed for some other reason
 UnlockError - base class for all unlocking exceptions
 AlreadyUnlocked - File was not locked.
 NotMyLock - File was locked but not by the current thread/process
 """
 
 from __future__ import absolute_import
 
 import functools
 import os
 import socket
 import threading
 import warnings
 
 # Work with PEP8 and non-PEP8 versions of threading module.
 if not hasattr(threading, "current_thread"):
 threading.current_thread = threading.currentThread
 if not hasattr(threading.Thread, "get_name"):
 threading.Thread.get_name = threading.Thread.getName
 
 __all__ = ['Error', 'LockError', 'LockTimeout', 'AlreadyLocked',
 'LockFailed', 'UnlockError', 'NotLocked', 'NotMyLock',
 'LinkFileLock', 'MkdirFileLock', 'SQLiteFileLock',
 'LockBase', 'locked']
 
 
 class Error(Exception):
 """
 Base class for other exceptions.
 
 >>> try:
 ...   raise Error
 ... except Exception:
 ...   pass
 """
 pass
 
 
 class LockError(Error):
 """
 Base class for error arising from attempts to acquire the lock.
 
 >>> try:
 ...   raise LockError
 ... except Error:
 ...   pass
 """
 pass
 
 
 class LockTimeout(LockError):
 """Raised when lock creation fails within a user-defined period of time.
 
 >>> try:
 ...   raise LockTimeout
 ... except LockError:
 ...   pass
 """
 pass
 
 
 class AlreadyLocked(LockError):
 """Some other thread/process is locking the file.
 
 >>> try:
 ...   raise AlreadyLocked
 ... except LockError:
 ...   pass
 """
 pass
 
 
 class LockFailed(LockError):
 """Lock file creation failed for some other reason.
 
 >>> try:
 ...   raise LockFailed
 ... except LockError:
 ...   pass
 """
 pass
 
 
 class UnlockError(Error):
 """
 Base class for errors arising from attempts to release the lock.
 
 >>> try:
 ...   raise UnlockError
 ... except Error:
 ...   pass
 """
 pass
 
 
 class NotLocked(UnlockError):
 """Raised when an attempt is made to unlock an unlocked file.
 
 >>> try:
 ...   raise NotLocked
 ... except UnlockError:
 ...   pass
 """
 pass
 
 
 class NotMyLock(UnlockError):
 """Raised when an attempt is made to unlock a file someone else locked.
 
 >>> try:
 ...   raise NotMyLock
 ... except UnlockError:
 ...   pass
 """
 pass
 
 
 class _SharedBase(object):
 def __init__(self, path):
 self.path = path
 
 def acquire(self, timeout=None):
 """
 Acquire the lock.
 
 * If timeout is omitted (or None), wait forever trying to lock the
 file.
 
 * If timeout > 0, try to acquire the lock for that many seconds.  If
 the lock period expires and the file is still locked, raise
 LockTimeout.
 
 * If timeout <= 0, raise AlreadyLocked immediately if the file is
 already locked.
 """
 raise NotImplemented("implement in subclass")
 
 def release(self):
 """
 Release the lock.
 
 If the file is not locked, raise NotLocked.
 """
 raise NotImplemented("implement in subclass")
 
 def __enter__(self):
 """
 Context manager support.
 """
 self.acquire()
 return self
 
 def __exit__(self, *_exc):
 """
 Context manager support.
 """
 self.release()
 
 def __repr__(self):
 return "<%s: %r>" % (self.__class__.__name__, self.path)
 
 
 class LockBase(_SharedBase):
 """Base class for platform-specific lock classes."""
 def __init__(self, path, threaded=True, timeout=None):
 """
 >>> lock = LockBase('somefile')
 >>> lock = LockBase('somefile', threaded=False)
 """
 super(LockBase, self).__init__(path)
 self.lock_file = os.path.abspath(path) + ".lock"
 self.hostname = socket.gethostname()
 self.pid = os.getpid()
 if threaded:
 t = threading.current_thread()
 # Thread objects in Python 2.4 and earlier do not have ident
 # attrs.  Worm around that.
 ident = getattr(t, "ident", hash(t))
 self.tname = "-%x" % (ident & 0xffffffff)
 else:
 self.tname = ""
 dirname = os.path.dirname(self.lock_file)
 
 # unique name is mostly about the current process, but must
 # also contain the path -- otherwise, two adjacent locked
 # files conflict (one file gets locked, creating lock-file and
 # unique file, the other one gets locked, creating lock-file
 # and overwriting the already existing lock-file, then one
 # gets unlocked, deleting both lock-file and unique file,
 # finally the last lock errors out upon releasing.
 self.unique_name = os.path.join(dirname,
 "%s%s.%s%s" % (self.hostname,
 self.tname,
 self.pid,
 hash(self.path)))
 self.timeout = timeout
 
 def is_locked(self):
 """
 Tell whether or not the file is locked.
 """
 raise NotImplemented("implement in subclass")
 
 def i_am_locking(self):
 """
 Return True if this object is locking the file.
 """
 raise NotImplemented("implement in subclass")
 
 def break_lock(self):
 """
 Remove a lock.  Useful if a locking thread failed to unlock.
 """
 raise NotImplemented("implement in subclass")
 
 def __repr__(self):
 return "<%s: %r -- %r>" % (self.__class__.__name__, self.unique_name,
 self.path)
 
 
 def _fl_helper(cls, mod, *args, **kwds):
 warnings.warn("Import from %s module instead of lockfile package" % mod,
 DeprecationWarning, stacklevel=2)
 # This is a bit funky, but it's only for awhile.  The way the unit tests
 # are constructed this function winds up as an unbound method, so it
 # actually takes three args, not two.  We want to toss out self.
 if not isinstance(args[0], str):
 # We are testing, avoid the first arg
 args = args[1:]
 if len(args) == 1 and not kwds:
 kwds["threaded"] = True
 return cls(*args, **kwds)
 
 
 def LinkFileLock(*args, **kwds):
 """Factory function provided for backwards compatibility.
 
 Do not use in new code.  Instead, import LinkLockFile from the
 lockfile.linklockfile module.
 """
 from . import linklockfile
 return _fl_helper(linklockfile.LinkLockFile, "lockfile.linklockfile",
 *args, **kwds)
 
 
 def MkdirFileLock(*args, **kwds):
 """Factory function provided for backwards compatibility.
 
 Do not use in new code.  Instead, import MkdirLockFile from the
 lockfile.mkdirlockfile module.
 """
 from . import mkdirlockfile
 return _fl_helper(mkdirlockfile.MkdirLockFile, "lockfile.mkdirlockfile",
 *args, **kwds)
 
 
 def SQLiteFileLock(*args, **kwds):
 """Factory function provided for backwards compatibility.
 
 Do not use in new code.  Instead, import SQLiteLockFile from the
 lockfile.mkdirlockfile module.
 """
 from . import sqlitelockfile
 return _fl_helper(sqlitelockfile.SQLiteLockFile, "lockfile.sqlitelockfile",
 *args, **kwds)
 
 
 def locked(path, timeout=None):
 """Decorator which enables locks for decorated function.
 
 Arguments:
 - path: path for lockfile.
 - timeout (optional): Timeout for acquiring lock.
 
 Usage:
 @locked('/var/run/myname', timeout=0)
 def myname(...):
 ...
 """
 def decor(func):
 @functools.wraps(func)
 def wrapper(*args, **kwargs):
 lock = FileLock(path, timeout=timeout)
 lock.acquire()
 try:
 return func(*args, **kwargs)
 finally:
 lock.release()
 return wrapper
 return decor
 
 
 if hasattr(os, "link"):
 from . import linklockfile as _llf
 LockFile = _llf.LinkLockFile
 else:
 from . import mkdirlockfile as _mlf
 LockFile = _mlf.MkdirLockFile
 
 FileLock = LockFile
 
 |