summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy McCurdy <andy@andymccurdy.com>2010-03-18 10:19:54 -0700
committerAndy McCurdy <andy@andymccurdy.com>2010-03-18 10:19:54 -0700
commit97ed01d9ac6fe39a53dd9414085f5aa0e44f6041 (patch)
tree70045e9decada18918adedd1a891af2c759f3593
parent5723a5468c4c168e798cecd72d21922f9c53d2e8 (diff)
downloadredis-py-97ed01d9ac6fe39a53dd9414085f5aa0e44f6041.tar.gz
HEXISTS and HLEN implementations
added a socket_timeout parameter to allow commands to timeout and raise an error
-rw-r--r--CHANGES7
-rw-r--r--redis/client.py38
-rw-r--r--setup.py2
-rw-r--r--tests/server_commands.py27
4 files changed, 61 insertions, 13 deletions
diff --git a/CHANGES b/CHANGES
index 513c108..0d1955b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,10 @@
+* 1.3.6
+ * Implementation of all Hash commands
+ * Pipelines now wrap their execution with MULTI and EXEC commands to
+ process all commands atomically.
+ * Connections can now set timeout. If command execution exceeds the
+ timeout, an exception is raised.
+ * Numerous bug fixes and more tests.
* 1.3.4
* Skipped version numbers ahead so that the client version matches the
Redis version it is feature-compatible with. Going forward, the client
diff --git a/redis/client.py b/redis/client.py
index ae04205..f3a8c67 100644
--- a/redis/client.py
+++ b/redis/client.py
@@ -15,11 +15,12 @@ class ConnectionManager(threading.local):
"Create a unique key for the specified host, port and db"
return '%s:%s:%s' % (host, port, db)
- def get_connection(self, host, port, db, password):
+ def get_connection(self, host, port, db, password, socket_timeout):
"Return a specific connection for the specified host, port and db"
key = self.make_connection_key(host, port, db)
if key not in self.connections:
- self.connections[key] = Connection(host, port, db, password)
+ self.connections[key] = Connection(
+ host, port, db, password, socket_timeout)
return self.connections[key]
def get_all_connections(self):
@@ -30,11 +31,13 @@ connection_manager = ConnectionManager()
class Connection(object):
"Manages TCP communication to and from a Redis server"
- def __init__(self, host='localhost', port=6379, db=0, password=None):
+ def __init__(self, host='localhost', port=6379, db=0, password=None,
+ socket_timeout=None):
self.host = host
self.port = port
self.db = db
self.password = password
+ self.socket_timeout = socket_timeout
self._sock = None
self._fp = None
@@ -49,6 +52,7 @@ class Connection(object):
raise ConnectionError("Error %s connecting to %s:%s. %s." % \
(e.args[0], self.host, self.port, e.args[1]))
sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
+ sock.settimeout(self.socket_timeout)
self._sock = sock
self._fp = sock.makefile('r')
redis_instance._setup_connection()
@@ -179,13 +183,13 @@ class Redis(threading.local):
"""
RESPONSE_CALLBACKS = dict_merge(
string_keys_to_dict(
- 'AUTH DEL EXISTS EXPIRE HDEL MOVE MSETNX RENAMENX '
+ 'AUTH DEL EXISTS EXPIRE HDEL HEXISTS MOVE MSETNX RENAMENX '
'SADD SISMEMBER SMOVE SETNX SREM ZADD ZREM',
bool
),
string_keys_to_dict(
- 'DECRBY INCRBY LLEN SCARD SDIFFSTORE SINTERSTORE SUNIONSTORE '
- 'ZCARD ZRANK ZREMRANGEBYSCORE ZREVRANK',
+ 'DECRBY HLEN INCRBY LLEN SCARD SDIFFSTORE SINTERSTORE '
+ 'SUNIONSTORE ZCARD ZRANK ZREMRANGEBYSCORE ZREVRANK',
int
),
string_keys_to_dict(
@@ -216,11 +220,11 @@ class Redis(threading.local):
)
def __init__(self, host='localhost', port=6379,
- db=0, password=None,
+ db=0, password=None, socket_timeout=None,
charset='utf-8', errors='strict'):
self.encoding = charset
self.errors = errors
- self.select(host, port, db, password)
+ self.select(host, port, db, password, socket_timeout)
#### Legacty accessors of connection information ####
def _get_host(self):
@@ -350,9 +354,10 @@ class Redis(threading.local):
)
#### CONNECTION HANDLING ####
- def get_connection(self, host, port, db, password):
+ def get_connection(self, host, port, db, password, socket_timeout):
"Returns a connection object"
- conn = connection_manager.get_connection(host, port, db, password)
+ conn = connection_manager.get_connection(
+ host, port, db, password, socket_timeout)
# if for whatever reason the connection gets a bad password, make
# sure a subsequent attempt with the right password makes its way
# to the connection
@@ -370,7 +375,7 @@ class Redis(threading.local):
raise AuthenticationError("Invalid Password")
self.format_inline('SELECT', self.connection.db)
- def select(self, host, port, db, password=None):
+ def select(self, host, port, db, password=None, socket_timeout=None):
"""
Switch to a different database on the current host/port
@@ -378,7 +383,8 @@ class Redis(threading.local):
prior to issuing the SELECT command. This makes sure we protect
the thread-safe connections
"""
- self.connection = self.get_connection(host, port, db, password)
+ self.connection = self.get_connection(
+ host, port, db, password, socket_timeout)
#### SERVER INFORMATION ####
def bgsave(self):
@@ -920,6 +926,10 @@ class Redis(threading.local):
def hdel(self, name, key):
"Delete ``key`` from hash ``name``"
return self.format_bulk('HDEL', name, key)
+
+ def hexists(self, name, key):
+ "Returns a boolean indicating if ``key`` exists within hash ``name``"
+ return self.format_bulk('HEXISTS', name, key)
def hget(self, name, key):
"Return the value of ``key`` within the hash ``name``"
@@ -933,6 +943,10 @@ class Redis(threading.local):
"Return the list of keys within hash ``name``"
return self.format_inline('HKEYS', name)
+ def hlen(self, name):
+ "Return the number of elements in hash ``name``"
+ return self.format_inline('HLEN', name)
+
def hset(self, name, key, value):
"""
Set ``key`` to ``value`` within hash ``name``
diff --git a/setup.py b/setup.py
index 5304cf0..0660371 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@
@brief Setuptools configuration for redis client
"""
-version = '1.34.1'
+version = '1.36'
sdict = {
'name' : 'redis',
diff --git a/tests/server_commands.py b/tests/server_commands.py
index 4d0fa20..aca8a17 100644
--- a/tests/server_commands.py
+++ b/tests/server_commands.py
@@ -728,6 +728,20 @@ class ServerCommandsTestCase(unittest.TestCase):
self.assert_(self.client.hdel('a', 'a2'))
self.assertEquals(self.client.hget('a', 'a2'), None)
+ def test_hexists(self):
+ # key is not a hash
+ self.client['a'] = 'a'
+ self.assertRaises(redis.ResponseError, self.client.hexists, 'a', 'a1')
+ del self.client['a']
+ # no key
+ self.assertEquals(self.client.hexists('a', 'a1'), False)
+ # real logic
+ self.make_hash('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(self.client.hexists('a', 'a1'), True)
+ self.assertEquals(self.client.hexists('a', 'a4'), False)
+ self.client.hdel('a', 'a1')
+ self.assertEquals(self.client.hexists('a', 'a1'), False)
+
def test_hgetall(self):
# key is not a hash
self.client['a'] = 'a'
@@ -758,6 +772,19 @@ class ServerCommandsTestCase(unittest.TestCase):
remote_keys.sort()
self.assertEquals(keys, remote_keys)
+ def test_hlen(self):
+ # key is not a hash
+ self.client['a'] = 'a'
+ self.assertRaises(redis.ResponseError, self.client.hlen, 'a')
+ del self.client['a']
+ # no key
+ self.assertEquals(self.client.hlen('a'), 0)
+ # real logic
+ self.make_hash('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(self.client.hlen('a'), 3)
+ self.client.hdel('a', 'a3')
+ self.assertEquals(self.client.hlen('a'), 2)
+
def test_hvals(self):
# key is not a hash
self.client['a'] = 'a'