import redis
import time

class LockTimeout(BaseException):
	pass

class Lock(object):

	"""
	Implements a distributed lock using Redis.
	"""

	def __init__(self, redis, lock_type, key, expires=60, timeout=10):
		self.key = key
		self.lock_type = lock_type
		self.redis = redis
		self.timeout = timeout
		self.expires = expires

	def lock_key(self):
		return "%s:locks:%s" % (self.lock_type,self.key)

	def __enter__(self):
		timeout = self.timeout
		while timeout >= 0:
			expires = time.time() + self.expires + 1
			pipe = self.redis.pipeline()
			lock_key = self.lock_key()
			pipe.watch(lock_key)
			try:
				lock_value = float(self.redis.get(lock_key))
			except (ValueError,TypeError):
				lock_value = None
			if not lock_value or lock_value < time.time():
				try:
					pipe.multi()
					pipe.set(lock_key,expires)
					pipe.expire(lock_key,self.expires+1)
					pipe.execute()
					return expires
				except redis.WatchError:
					print "Someone tinkered with the lock!"
					pass
			timeout -= 0.01
			time.sleep(0.01)
		raise LockTimeout("Timeout whilst waiting for lock")

	def __exit__(self, exc_type, exc_value, traceback):
		self.redis.delete(self.lock_key())

if __name__ == '__main__':
        server = redis.Redis("localhost")
        with Lock(server,"events","user:abc",expires = 10) as lock:
                print "locked"
                time.sleep(20)
        print "unlocked!"