diff options
-rw-r--r-- | redis/client.py | 54 | ||||
-rw-r--r-- | tests/lock.py | 10 |
2 files changed, 46 insertions, 18 deletions
diff --git a/redis/client.py b/redis/client.py index 4f178a1..b4864b5 100644 --- a/redis/client.py +++ b/redis/client.py @@ -283,12 +283,19 @@ class Redis(threading.local): self.errors ) - def lock(self, name): + def lock(self, name, timeout=None, sleep=0.1): """ Return a new Lock object using key ``name`` that mimics - the behavior of threading.Lock + the behavior of threading.Lock. + + If specified, ``timeout`` indicates a maximum life for the lock. + By default, it will remain locked until release() is called. + + ``sleep`` indicates the amount of time to sleep per loop iteration + when the lock is in blocking mode and another client is currently + holding the lock. """ - return Lock(self, name) + return Lock(self, name, timeout=timeout, sleep=sleep) #### COMMAND EXECUTION AND PROTOCOL PARSING #### def _execute_command(self, command_name, command, **options): @@ -799,7 +806,7 @@ class Redis(threading.local): return self.execute_command('RPUSH', name, value) def sort(self, name, start=None, num=None, by=None, get=None, - desc=False, alpha=False, store=None): + desc=False, alpha=False, store=None): """ Sort and return the list, set or sorted set at ``name``. @@ -1310,29 +1317,44 @@ class Lock(object): LOCK_FOREVER = 2**31+1 # 1 past max unix time - def __init__(self, redis, name): + def __init__(self, redis, name, timeout=None, sleep=0.1): + """ + Create a new Lock instnace named ``name`` using the Redis client + supplied by ``redis``. + + ``timeout`` indicates a maximum life for the lock. + By default, it will remain locked until release() is called. + + ``sleep`` indicates the amount of time to sleep per loop iteration + when the lock is in blocking mode and another client is currently + holding the lock. + + Note: If using ``timeout``, you should make sure all the hosts + that are running clients are within the same timezone and are using + a network time service like ntp. + """ self.redis = redis self.name = name self.acquired_until = None + self.timeout = timeout + self.sleep = sleep + + def __enter__(self): + return self.acquire() - def acquire(self, blocking=True, timeout=None, sleep=0.1): + def __exit__(self, exc_type, exc_value, traceback): + self.release() + + def acquire(self, blocking=True): """ Use Redis to hold a shared, distributed lock named ``name``. Returns True once the lock is acquired. If ``blocking`` is False, always return immediately. If the lock was acquired, return True, otherwise return False. - - ``timeout`` indicates the maxium lifetime of the lock. If None, - lock forever. - - ``sleep`` indicates the the amount of time to sleep during each loop - while attempting to acquire the lock when ``blocking``=True - - Note: If using ``timeout``, you should make sure all the hosts - that are running clients are within the same timezone and are using - a network time service like ntp. """ + sleep = self.sleep + timeout = self.timeout while 1: unixtime = int(time.time()) if timeout: diff --git a/tests/lock.py b/tests/lock.py index a6ec9e7..a6e447d 100644 --- a/tests/lock.py +++ b/tests/lock.py @@ -1,3 +1,4 @@ +from __future__ import with_statement import redis import time import unittest @@ -29,9 +30,9 @@ class LockTestCase(unittest.TestCase): lock2.release() def test_timeouts(self): - lock1 = self.client.lock('foo') + lock1 = self.client.lock('foo', timeout=1) lock2 = self.client.lock('foo') - self.assert_(lock1.acquire(timeout=1)) + self.assert_(lock1.acquire()) self.assertEquals(lock1.acquired_until, long(time.time()) + 1) self.assertEquals(lock1.acquired_until, long(self.client['foo'])) self.assertFalse(lock2.acquire(blocking=False)) @@ -45,3 +46,8 @@ class LockTestCase(unittest.TestCase): self.assert_(lock1.acquired_until) lock1.release() self.assert_(lock1.acquired_until is None) + + def test_context_manager(self): + with self.client.lock('foo'): + self.assertEquals(self.client['foo'], str(Lock.LOCK_FOREVER)) + self.assertEquals(self.client['foo'], None) |