Skip to content

Commit

Permalink
Update behavior of lock to behave closer to redis lock
Browse files Browse the repository at this point in the history
  • Loading branch information
taylor-cedar committed Sep 17, 2018
1 parent b87e885 commit 6fc7375
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 7 deletions.
20 changes: 13 additions & 7 deletions fakeredis.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
from datetime import datetime, timedelta
import operator
import sys
import threading
import time
import types
import re
import functools
from itertools import count, islice
from uuid import uuid4

import redis
from redis.exceptions import ResponseError
from redis.exceptions import ResponseError, LockError
import redis.client

try:
Expand Down Expand Up @@ -315,8 +315,8 @@ class _Lock(object):
def __init__(self, redis, name, timeout):
self.redis = redis
self.name = name
self.lock = threading.Lock()
redis.set(name, self, ex=timeout)
self.timeout = timeout
self.id = str(uuid4())

def __enter__(self):
self.acquire()
Expand All @@ -326,11 +326,17 @@ def __exit__(self, exc_type, exc_value, traceback):
self.release()

def acquire(self, blocking=True, blocking_timeout=None):
return self.lock.acquire(blocking)
acquired = bool(self.redis.set(self.name, self.id, nx=True, ex=self.timeout))
if not acquired and blocking:
raise ValueError('fakeredis can\'t do blocking locks')

return acquired

def release(self):
self.lock.release()
self.redis.delete(self.name)
if _decode(self.redis.get(self.name)) == self.id:
self.redis.delete(self.name)
else:
raise LockError('Cannot release an unlocked lock')


def _check_conn(func):
Expand Down
32 changes: 32 additions & 0 deletions test_fakeredis.py
Original file line number Diff line number Diff line change
Expand Up @@ -3916,6 +3916,38 @@ def test_lock(self):
self.assertTrue(self.redis.exists('bar'))
self.assertFalse(self.redis.exists('bar'))

def test_acquiring_lock_twice(self):
lock = self.redis.lock('foo')
self.assertTrue(lock.acquire(blocking=False))
self.assertFalse(lock.acquire(blocking=False))

def test_acquiring_lock_different_lock(self):
lock1 = self.redis.lock('foo')
lock2 = self.redis.lock('foo')
self.assertTrue(lock1.acquire(blocking=False))
self.assertFalse(lock2.acquire(blocking=False))

def test_acquiring_lock_different_lock_release(self):
lock1 = self.redis.lock('foo')
lock2 = self.redis.lock('foo')
self.assertTrue(lock1.acquire(blocking=False))
self.assertFalse(lock2.acquire(blocking=False))

# Test only releasing lock1 actually releases the lock
with self.assertRaises(redis.exceptions.LockError):
lock2.release()
self.assertFalse(lock2.acquire(blocking=False))
lock1.release()

# Locking with lock2 now has the lock
self.assertTrue(lock2.acquire(blocking=False))
self.assertFalse(lock1.acquire(blocking=False))

def test_nested_lock(self):
with self.redis.lock('bar'):
acquired = self.redis.lock('bar').acquire(blocking=False)
self.assertFalse(acquired)


class DecodeMixin(object):
decode_responses = True
Expand Down

0 comments on commit 6fc7375

Please sign in to comment.