lockfiles.py
72 lines
| 2.4 KiB
| text/x-python
|
PythonLexer
r2007 | import os | |||
class LockFile(object): | ||||
r3449 | """Provides methods to obtain, check for, and release a file based lock which | |||
should be used to handle concurrent access to the same file. | ||||
r2007 | ||||
r3449 | As we are a utility class to be derived from, we only use protected methods. | |||
r2007 | ||||
r3449 | Locks will automatically be released on destruction""" | |||
__slots__ = ("_file_path", "_owns_lock") | ||||
r2007 | ||||
r3449 | def __init__(self, file_path): | |||
self._file_path = file_path | ||||
self._owns_lock = False | ||||
r2007 | ||||
r3449 | def __del__(self): | |||
self._release_lock() | ||||
r2007 | ||||
r3449 | def _lock_file_path(self): | |||
""":return: Path to lockfile""" | ||||
return "%s.lock" % (self._file_path) | ||||
r2007 | ||||
r3449 | def _has_lock(self): | |||
""":return: True if we have a lock and if the lockfile still exists | ||||
:raise AssertionError: if our lock-file does not exist""" | ||||
if not self._owns_lock: | ||||
return False | ||||
r2007 | ||||
r3449 | return True | |||
r2007 | ||||
r3449 | def _obtain_lock_or_raise(self): | |||
"""Create a lock file as flag for other instances, mark our instance as lock-holder | ||||
r2007 | ||||
r3449 | :raise IOError: if a lock was already present or a lock file could not be written""" | |||
if self._has_lock(): | ||||
return | ||||
lock_file = self._lock_file_path() | ||||
if os.path.isfile(lock_file): | ||||
raise IOError("Lock for file %r did already exist, delete %r in case the lock is illegal" % (self._file_path, lock_file)) | ||||
r2007 | ||||
r3449 | try: | |||
fd = os.open(lock_file, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0) | ||||
os.close(fd) | ||||
except OSError,e: | ||||
raise IOError(str(e)) | ||||
r2007 | ||||
r3449 | self._owns_lock = True | |||
r2007 | ||||
r3449 | def _obtain_lock(self): | |||
"""The default implementation will raise if a lock cannot be obtained. | ||||
Subclasses may override this method to provide a different implementation""" | ||||
return self._obtain_lock_or_raise() | ||||
r2007 | ||||
r3449 | def _release_lock(self): | |||
"""Release our lock if we have one""" | ||||
if not self._has_lock(): | ||||
return | ||||
r2007 | ||||
r3449 | # if someone removed our file beforhand, lets just flag this issue | |||
# instead of failing, to make it more usable. | ||||
lfp = self._lock_file_path() | ||||
try: | ||||
# on bloody windows, the file needs write permissions to be removable. | ||||
# Why ... | ||||
if os.name == 'nt': | ||||
os.chmod(lfp, 0777) | ||||
# END handle win32 | ||||
os.remove(lfp) | ||||
except OSError: | ||||
pass | ||||
self._owns_lock = False | ||||