|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- # This module is taken from https://gist.github.com/ionrock/3015700
-
- # A file lock implementation that tries to avoid platform specific
- # issues. It is inspired by a whole bunch of different implementations
- # listed below.
-
- # - https://bitbucket.org/jaraco/yg.lockfile/src/6c448dcbf6e5/yg/lockfile/__init__.py
- # - http://svn.zope.org/zc.lockfile/trunk/src/zc/lockfile/__init__.py?rev=121133&view=markup
- # - http://stackoverflow.com/questions/489861/locking-a-file-in-python
- # - http://www.evanfosmark.com/2009/01/cross-platform-file-locking-support-in-python/
- # - http://packages.python.org/lockfile/lockfile.html
-
- # There are some tests below and a blog posting conceptually the
- # problems I wanted to try and solve. The tests reflect these ideas.
-
- # - http://ionrock.wordpress.com/2012/06/28/file-locking-in-python/
-
- # I'm not advocating using this package. But if you do happen to try it
- # out and have suggestions please let me know.
-
- import os
- import time
-
-
- class FileLocked(Exception):
- pass
-
-
- class FileLock(object):
-
- def __init__(self, lock_filename, timeout=None, force=False):
- self.lock_filename = '%s.lock' % lock_filename
- self.timeout = timeout
- self.force = force
- self._pid = str(os.getpid())
- # Store pid in a file in the same directory as desired lockname
- self.pid_filename = os.path.join(
- os.path.dirname(self.lock_filename),
- self._pid,
- ) + '.lock'
-
- def get_lock_pid(self):
- try:
- return int(open(self.lock_filename).read())
- except IOError:
- # If we can't read symbolic link, there are two possibilities:
- # 1. The symbolic link is dead (point to non existing file)
- # 2. Symbolic link is not there
- # In either case, we can safely release the lock
- self.release()
-
- def valid_lock(self):
- """
- See if the lock exists and is left over from an old process.
- """
-
- lock_pid = self.get_lock_pid()
-
- # If we're unable to get lock_pid
- if lock_pid is None:
- return False
-
- # this is our process
- if self._pid == lock_pid:
- return True
-
- # it is/was another process
- # see if it is running
- try:
- os.kill(lock_pid, 0)
- except OSError:
- self.release()
- return False
-
- # it is running
- return True
-
- def is_locked(self, force=False):
- # We aren't locked
- if not self.valid_lock():
- return False
-
- # We are locked, but we want to force it without waiting
- if not self.timeout:
- if self.force:
- self.release()
- return False
- else:
- # We're not waiting or forcing the lock
- raise FileLocked()
-
- # Locked, but want to wait for an unlock
- interval = .1
- intervals = int(self.timeout / interval)
-
- while intervals:
- if self.valid_lock():
- intervals -= 1
- time.sleep(interval)
- #print('stopping %s' % intervals)
- else:
- return True
-
- # check one last time
- if self.valid_lock():
- if self.force:
- self.release()
- else:
- # still locked :(
- raise FileLocked()
-
- def acquire(self):
- """Create a pid filename and create a symlink (the actual lock file)
- across platforms that points to it. Symlink is used because it's an
- atomic operation across platforms.
- """
-
- pid_file = os.open(self.pid_filename, os.O_CREAT | os.O_EXCL | os.O_RDWR)
- os.write(pid_file, str(os.getpid()).encode('utf-8'))
- os.close(pid_file)
-
- if hasattr(os, 'symlink'):
- os.symlink(self.pid_filename, self.lock_filename)
- else:
- # Windows platforms doesn't support symlinks, at least not through the os API
- self.lock_filename = self.pid_filename
-
-
- def release(self):
- """Try to delete the lock files. Doesn't matter if we fail"""
- if self.lock_filename != self.pid_filename:
- try:
- os.unlink(self.lock_filename)
- except OSError:
- pass
-
- try:
- os.remove(self.pid_filename)
- except OSError:
- pass
-
- def __enter__(self):
- if not self.is_locked():
- self.acquire()
- return self
-
- def __exit__(self, type, value, traceback):
- self.release()
|