summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy McCurdy <andy@andymccurdy.com>2011-06-01 00:44:37 -0700
committerAndy McCurdy <andy@andymccurdy.com>2011-06-01 00:44:37 -0700
commit47a4a7e339ea9c4243ea14303d7234f0de13f959 (patch)
tree475288f548daa3ae74df91650925294a64632908
parent8c75e85b9f14d65add7e7a27586aae2972220e1d (diff)
downloadredis-py-47a4a7e339ea9c4243ea14303d7234f0de13f959.tar.gz
added ability for per-instance response callbacks. also, holy shit, documentation!
-rw-r--r--README.md602
-rw-r--r--redis/client.py21
2 files changed, 146 insertions, 477 deletions
diff --git a/README.md b/README.md
index cf0d387..b4ee819 100644
--- a/README.md
+++ b/README.md
@@ -1,494 +1,150 @@
-redis-py
-========
+# redis-py
-This is the Python interface to the Redis key-value store.
+The Python interface to the Redis key-value store.
+## Installation
-Usage
------
-
- >>> import redis
- >>> r = redis.Redis(host='localhost', port=6379, db=0)
- >>> r.set('foo', 'bar') # or r['foo'] = 'bar'
- True
- >>> r.get('foo') # or r['foo']
- 'bar'
-
-For a complete list of commands, check out the list of Redis commands here:
-http://redis.io/commands
-
-Installation
-------------
$ sudo pip install redis
-alternatively:
+or alternatively (you really should be using pip though):
$ sudo easy_install redis
-From sources:
+From source:
$ sudo python setup.py install
-Versioning scheme
------------------
-
-redis-py is versioned after Redis. So, for example, redis-py 2.0.0 should
-support all the commands available in Redis 2.0.0.
-
-API Reference
--------------
-
-### append(self, key, value)
- Appends the string _value_ to the value at _key_. If _key_
- doesn't already exist, create it with a value of _value_.
- Returns the new length of the value at _key_.
-
-### bgrewriteaof(self)
- Tell the Redis server to rewrite the AOF file from data in memory.
-
-### bgsave(self)
- Tell the Redis server to save its data to disk. Unlike save(),
- this method is asynchronous and returns immediately.
-
-### blpop(self, keys, timeout=0)
- LPOP a value off of the first non-empty list
- named in the _keys_ list.
-
- If none of the lists in _keys_ has a value to LPOP, then block
- for _timeout_ seconds, or until a value gets pushed on to one
- of the lists.
-
- If timeout is 0, then block indefinitely.
-
-### brpop(self, keys, timeout=0)
- RPOP a value off of the first non-empty list
- named in the _keys_ list.
-
- If none of the lists in _keys_ has a value to LPOP, then block
- for _timeout_ seconds, or until a value gets pushed on to one
- of the lists.
-
- If timeout is 0, then block indefinitely.
-
-### dbsize(self)
- Returns the number of keys in the current database
-
-### decr(self, name, amount=1)
- Decrements the value of _key_ by _amount_. If no key exists,
- the value will be initialized as 0 - _amount_
-
-### delete(self, *names)
- Delete one or more keys specified by _names_
-
-### encode(self, value)
- Encode _value_ using the instance's charset
-
-### execute_command(self, *args, **options)
- Sends the command to the redis server and returns it's response
-
-### exists(self, name)
- Returns a boolean indicating whether key _name_ exists
-
-### expire(self, name, time)
- Set an expire flag on key _name_ for _time_ seconds
-
-### expireat(self, name, when)
- Set an expire flag on key _name_. _when_ can be represented
- as an integer indicating unix time or a Python datetime object.
-
-### flush(self, all_dbs=False)
-
-### flushall(self)
- Delete all keys in all databases on the current host
-
-### flushdb(self)
- Delete all keys in the current database
-
-### get(self, name)
- Return the value at key _name_, or None of the key doesn't exist
-### get_connection(self, host, port, db, password, socket_timeout)
- Returns a connection object
+## Getting Started
-### getset(self, name, value)
- Set the value at key _name_ to _value_ if key doesn't exist
- Return the value at key _name_ atomically
-
-### hdel(self, name, key)
- Delete _key_ from hash _name_
-
-### hexists(self, name, key)
- Returns a boolean indicating if _key_ exists within hash _name_
-
-### hget(self, name, key)
- Return the value of _key_ within the hash _name_
-
-### hgetall(self, name)
- Return a Python dict of the hash's name/value pairs
-
-### hincrby(self, name, key, amount=1)
- Increment the value of _key_ in hash _name_ by _amount_
-
-### hkeys(self, name)
- Return the list of keys within hash _name_
-
-### hlen(self, name)
- Return the number of elements in hash _name_
-
-### hmget(self, name, keys)
- Returns a list of values ordered identically to _keys_
-
-### hmset(self, name, mapping)
- Sets each key in the _mapping_ dict to its corresponding value
- in the hash _name_
-
-### hset(self, name, key, value)
- Set _key_ to _value_ within hash _name_
- Returns 1 if HSET created a new field, otherwise 0
-
-### hsetnx(self, name, key, value)
- Set _key_ to _value_ within hash _name_ if _key_ does not
- exist. Returns 1 if HSETNX created a field, otherwise 0.
-
-### hvals(self, name)
- Return the list of values within hash _name_
-
-### incr(self, name, amount=1)
- Increments the value of _key_ by _amount_. If no key exists,
- the value will be initialized as _amount_
-
-### info(self)
- Returns a dictionary containing information about the Redis server
-
-### keys(self, pattern='*')
- Returns a list of keys matching _pattern_
-
-### lastsave(self)
- Return a Python datetime object representing the last time the
- Redis database was saved to disk
-
-### lindex(self, name, index)
- Return the item from list _name_ at position _index_
-
- Negative indexes are supported and will return an item at the
- end of the list
-
-### linsert(self, name, where, refvalue, value)
- Insert _value_ in list _name_ either immediately before or after
- (_where_) _refvalue_.
-
- Returns the new length of the list on success or -1 if _refvalue_
- is not in the list.
-
-### listen(self)
- Listen for messages on channels this client has been subscribed to
-
-### llen(self, name)
- Return the length of the list _name_
-
-### lock(self, name, timeout=None, sleep=0.10000000000000001)
- Return a new Lock object using key _name_ that mimics
- 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.
+ >>> import redis
+ >>> r = redis.Redis(host='localhost', port=6379, db=0)
+ >>> r.set('foo', 'bar')
+ True
+ >>> r.get('foo')
+ 'bar'
-### lpop(self, name)
- Remove and return the first item of the list _name_
+## More Detail
-### lpush(self, name, value)
- Push _value_ onto the head of the list _name_
+### Connection Pools
-### lrange(self, name, start, end)
- Return a slice of the list _name_ between
- position _start_ and _end_
-
- _start_ and _end_ can be negative numbers just like
- Python slicing notation
+Behind the scenes, redis-py uses a connection pool to manage connections to
+a Redis server. By default, each Redis instance you create will in turn create
+its own connection pool. You can override this behavior and use an existing
+connection pool by passing an already created connection pool instance to the
+connection_pool argument of the Redis class. You may choose to do this in order
+to implement client side sharding or have finer grain control of how connections
+are managed.
-### lrem(self, name, value, num=0)
- Remove the first _num_ occurrences of _value_ from list _name_
-
- If _num_ is 0, then all occurrences will be removed
+ >>> pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
+ >>> r = redis.Redis(connection_pool=pool)
+
+### Connetions
+
+ConnectionPools manage a set of Connection instances. redis-py ships with two
+types of Connections. The default, Connection, is a normal TCP socket based
+connection. UnixDomainSocketConnection allows for clients running on the same
+device to connect via a unix domain socket. To use a
+UnixDomainSocketConnection connection, simply pass the class to the
+connection_class argument of either the Redis or ConnectionPool class. You must
+also specify the path argument, which is a string to the unix domain socket
+file. Additionally, make sure the unixsocket parameter is defined in your
+redis.conf file. It's commented out by default.
+
+ >>> r = redis.Redis(connection_class=redis.UnixDomainSocketConnection,
+ >>> path='/tmp/redis.sock')
+
+You can create your own Connection subclasses in this way as well. This may be
+useful if you want to control the socket behavior within an async framework.
+
+### Parsers
+
+Parser classes provide a way to control how responses from the Redis server
+are parsed. redis-py ships with two parse classes, the PythonParse and the
+HiredisParser. By default, redis-py will attempt to use the HiredisParser if
+you have the hiredis module installed and will fallback to the PythonParser
+otherwise.
+
+Hiredis is a C library maintained by the core Redis team. Pieter Noordhuis was
+kind enough to create Python bindings. Using Hiredis can provide up to a
+10x speed improvement. The performance increase is most noticeable when
+retrieving many pieces of data, such as from a ZRANGE or HGETALL operation.
+
+Hiredis is available on Pypi, and can be installed via pip or easy_install
+just like redis-py.
+
+ $ pip install hiredis
+
+or
+
+ $ easy_install hiredis
+
+### Response Callbacks
+
+The client class uses a set of callbacks to cast Redis responses to the
+appropriate Python type. There are a number of these callbacks defined on
+the Redis client class in a dictionary called RESPONSE_CALLBACKS.
+
+Custom callbacks can be added on a per-instance basis using the
+set_response_callback method. This method accepts two arguments: a command
+name and the callback. Callbacks added in this manner are only valid on the
+instance the callback is added to. If you want to define or override a callback
+globally, then you should look into making a subclass and added your callback
+to the REDIS_CALLBACKS class dictionary.
+
+Response callbacks take at least one parameter: the response from the Redis
+server. Keyword arguments may also be accepted in order to further control
+how to interpret the response. The keyword arguments are specified during the
+command's call to execute_command. The ZRANGE implementation demonstrates the
+use of response callback keyword arguments using the "withscores" argument.
+
+## Thread Safety
+
+Redis client instances can safely be shared between threads. Internally,
+connection instances are only retrieved from the connection pool during
+command execution, and returned to the pool directly after. Command execution
+never modifies state on the client instance.
+
+However, there is one caveat: the Redis SELECT command. The SELECT command
+allows you to switch to a separate database on the same Redis server. That
+database remains selected until another is selected. This creates a proble in
+that connections could be returned to the pool that are now set to a different
+database.
+
+As a result, redis-py does not implement the SELECT command on client instances.
+If you use multiple Redis databases, you should create a separate client
+instance (and possible a separate connection pool) to each database.
+
+## Versioning scheme
+
+redis-py is versioned after Redis. So, for example, redis-py 2.0.0 should
+support all the commands available in Redis 2.0.0.
+
+## API Reference
+
+The official Redis documentation does a great job of explaining each command in
+detail (http://redis.io/commands). In most cases, redis-py uses the same
+arguments as the official spec. There are a few exceptions noted here:
+
+* SELECT: Not implemented. See the explanation in the Thread Safety section
+ above.
+* ZADD: Redis specifies the 'score' argument before 'value'. These were swapped
+ accidentally when being implemented and not discovered until after people
+ were already using it. As of Redis 2.4, ZADD will start supporting variable
+ arguments. redis-py implements these as python keyword arguments, where the
+ name is the 'value' and the value is the 'score'.
+* DEL: 'del' is a reserved keyword in the Python syntax. Therefore redis-py
+ uses 'delete' instead.
+* CONFIG GET|SET: These are implemented separately config_get or config_set.
+* MULTI/EXEC: These are implemented as part of the Pipeline class. Calling
+ the pipeline method and specifying use_transaction=True will cause the
+ pipline to be wrapped with the MULTI and EXEC statements when it is executed.
+* SUBSCRIBE/LISTEN: Similar to pipelines, PubSub is implemented as a separate
+ class as it places the underlying connection in a state where it can't
+ execute non-pubsub commands. Calling the pubsub method from the Redis client
+ will return a PubSub instance where you can subscribe to channels and listen
+ for messages. You can call PUBLISH from both classes.
-### lset(self, name, index, value)
- Set _position_ of list _name_ to _value_
-
-### ltrim(self, name, start, end)
- Trim the list _name_, removing all values not within the slice
- between _start_ and _end_
-
- _start_ and _end_ can be negative numbers just like
- Python slicing notation
-
-### mget(self, keys, *args)
- Returns a list of values ordered identically to _keys_
-
- * Passing *args to this method has been deprecated *
-
-### move(self, name, db)
- Moves the key _name_ to a different Redis database _db_
-
-### mset(self, mapping)
- Sets each key in the _mapping_ dict to its corresponding value
-
-### msetnx(self, mapping)
- Sets each key in the _mapping_ dict to its corresponding value if
- none of the keys are already set
-
-### parse_response(self, command_name, catch_errors=False, **options)
- Parses a response from the Redis server
-
-### ping(self)
- Ping the Redis server
-
-### pipeline(self, transaction=True)
- Return a new pipeline object that can queue multiple commands for
- later execution. _transaction_ indicates whether all commands
- should be executed atomically. Apart from multiple atomic operations,
- pipelines are useful for batch loading of data as they reduce the
- number of back and forth network operations between client and server.
-
-### pop(self, name, tail=False)
- Pop and return the first or last element of list _name_
-
- This method has been deprecated, use _Redis.lpop_ or _Redis.rpop_ instead.
-
-### psubscribe(self, patterns)
- Subscribe to all channels matching any pattern in _patterns_
-
-### publish(self, channel, message)
- Publish _message_ on _channel_.
- Returns the number of subscribers the message was delivered to.
-
-### punsubscribe(self, patterns=[])
- Unsubscribe from any channel matching any pattern in _patterns_.
- If empty, unsubscribe from all channels.
-
-### push(self, name, value, head=False)
- Push _value_ onto list _name_.
-
- This method has been deprecated, use __Redis.lpush__ or __Redis.rpush__ instead.
-
-### randomkey(self)
- Returns the name of a random key
-
-### rename(self, src, dst, **kwargs)
- Rename key _src_ to _dst_
-
- * The following flags have been deprecated *
- If _preserve_ is True, rename the key only if the destination name
- doesn't already exist
-
-### renamenx(self, src, dst)
- Rename key _src_ to _dst_ if _dst_ doesn't already exist
-
-### rpop(self, name)
- Remove and return the last item of the list _name_
-
-### rpoplpush(self, src, dst)
- RPOP a value off of the _src_ list and atomically LPUSH it
- on to the _dst_ list. Returns the value.
-
-### rpush(self, name, value)
- Push _value_ onto the tail of the list _name_
-
-### sadd(self, name, value)
- Add _value_ to set _name_
-
-### save(self)
- Tell the Redis server to save its data to disk,
- blocking until the save is complete
-
-### scard(self, name)
- Return the number of elements in set _name_
-
-### sdiff(self, keys, *args)
- Return the difference of sets specified by _keys_
-
-### sdiffstore(self, dest, keys, *args)
- Store the difference of sets specified by _keys_ into a new
- set named _dest_. Returns the number of keys in the new set.
-
-### select(self, db, host=None, port=None, password=None, socket_timeout=None)
- Switch to a different Redis connection.
-
- If the host and port aren't provided and there's an existing
- connection, use the existing connection's host and port instead.
-
- Note this method actually replaces the underlying connection object
- prior to issuing the SELECT command. This makes sure we protect
- the thread-safe connections
-
-### set(self, name, value, **kwargs)
- Set the value at key _name_ to _value_
-
- * The following flags have been deprecated *
- If _preserve_ is True, set the value only if key doesn't already
- exist
- If _getset_ is True, set the value only if key doesn't already exist
- and return the resulting value of key
-
-### setex(self, name, value, time)
- Set the value of key _name_ to _value_
- that expires in _time_ seconds
-
-### setnx(self, name, value)
- Set the value of key _name_ to _value_ if key doesn't exist
-
-### sinter(self, keys, *args)
- Return the intersection of sets specified by _keys_
-
-### sinterstore(self, dest, keys, *args)
- Store the intersection of sets specified by _keys_ into a new
- set named _dest_. Returns the number of keys in the new set.
-
-### sismember(self, name, value)
- Return a boolean indicating if _value_ is a member of set _name_
-
-### smembers(self, name)
- Return all members of the set _name_
-
-### smove(self, src, dst, value)
- Move _value_ from set _src_ to set _dst_ atomically
-
-### sort(self, name, start=None, num=None, by=None, get=None, desc=False, alpha=False, store=None)
- Sort and return the list, set or sorted set at _name_.
-
- _start_ and _num_ allow for paging through the sorted data
-
- _by_ allows using an external key to weight and sort the items.
- Use an "*" to indicate where in the key the item value is located
-
- _get_ allows for returning items from external keys rather than the
- sorted data itself. Use an "*" to indicate where int he key
- the item value is located
-
- _desc_ allows for reversing the sort
-
- _alpha_ allows for sorting lexicographically rather than numerically
-
- _store_ allows for storing the result of the sort into
- the key _store_
-
-### spop(self, name)
- Remove and return a random member of set _name_
-
-### srandmember(self, name)
- Return a random member of set _name_
-
-### srem(self, name, value)
- Remove _value_ from set _name_
-
-### subscribe(self, channels)
- Subscribe to _channels_, waiting for messages to be published
-
-### substr(self, name, start, end=-1)
- Return a substring of the string at key _name_. _start_ and _end_
- are 0-based integers specifying the portion of the string to return.
-
-### sunion(self, keys, *args)
- Return the union of sets specifiued by _keys_
-
-### sunionstore(self, dest, keys, *args)
- Store the union of sets specified by _keys_ into a new
- set named _dest_. Returns the number of keys in the new set.
-
-### ttl(self, name)
- Returns the number of seconds until the key _name_ will expire
-
-### type(self, name)
- Returns the type of key _name_
-
-### unsubscribe(self, channels=[])
- Unsubscribe from _channels_. If empty, unsubscribe
- from all channels
-
-### watch(self, name):
- Watches the value at key _name_.
-
-### zadd(self, name, value, score)
- Add member _value_ with score _score_ to sorted set _name_
-
-### zcard(self, name)
- Return the number of elements in the sorted set _name_
-
-### zincr(self, key, member, value=1)
- This has been deprecated, use zincrby instead
-
-### zincrby(self, name, value, amount=1)
- Increment the score of _value_ in sorted set _name_ by _amount_
-
-### zinter(self, dest, keys, aggregate=None)
-
-###zinterstore(self, dest, keys, aggregate=None)
- Intersect multiple sorted sets specified by _keys_ into
- a new sorted set, _dest_. Scores in the destination will be
- aggregated based on the _aggregate_, or SUM if none is provided.
-
-### zrange(self, name, start, end, desc=False, withscores=False)
- Return a range of values from sorted set _name_ between
- _start_ and _end_ sorted in ascending order.
-
- _start_ and _end_ can be negative, indicating the end of the range.
-
- _desc_ indicates to sort in descending order.
-
- _withscores_ indicates to return the scores along with the values.
- The return type is a list of (value, score) pairs
-
-### zrangebyscore(self, name, min, max, start=None, num=None, withscores=False)
- Return a range of values from the sorted set _name_ with scores
- between _min_ and _max_.
-
- If _start_ and _num_ are specified, then return a slice of the range.
-
- _withscores_ indicates to return the scores along with the values.
- The return type is a list of (value, score) pairs
-
-### zrank(self, name, value)
- Returns a 0-based value indicating the rank of _value_ in sorted set
- _name_
-
-### zrem(self, name, value)
- Remove member _value_ from sorted set _name_
-
-### zremrangebyrank(self, name, min, max)
- Remove all elements in the sorted set _name_ with ranks between
- _min_ and _max_. Values are 0-based, ordered from smallest score
- to largest. Values can be negative indicating the highest scores.
- Returns the number of elements removed
-
-### zremrangebyscore(self, name, min, max)
- Remove all elements in the sorted set _name_ with scores
- between _min_ and _max_. Returns the number of elements removed.
-
-### zrevrange(self, name, start, num, withscores=False)
- Return a range of values from sorted set _name_ between
- _start_ and _num_ sorted in descending order.
-
- _start_ and _num_ can be negative, indicating the end of the range.
-
- _withscores_ indicates to return the scores along with the values
- as a dictionary of value => score
-
-### zrevrank(self, name, value)
- Returns a 0-based value indicating the descending rank of
- _value_ in sorted set _name_
-
-### zscore(self, name, value)
- Return the score of element _value_ in sorted set _name_
-
-### zunion(self, dest, keys, aggregate=None)
-
-### zunionstore(self, dest, keys, aggregate=None)
- Union multiple sorted sets specified by _keys_ into
- a new sorted set, _dest_. Scores in the destination will be
- aggregated based on the _aggregate_, or SUM if none is provided.
Author
------
diff --git a/redis/client.py b/redis/client.py
index 1ab02ca..c14499c 100644
--- a/redis/client.py
+++ b/redis/client.py
@@ -182,6 +182,12 @@ class Redis(object):
connection_pool = ConnectionPool(**kwargs)
self.connection_pool = connection_pool
+ self.response_callbacks = self.__class__.RESPONSE_CALLBACKS.copy()
+
+ def set_response_callback(self, command, callback):
+ "Set a custom Response Callback"
+ self.response_callbacks[command] = callback
+
def pipeline(self, transaction=True, shard_hint=None):
"""
Return a new pipeline object that can queue multiple commands for
@@ -233,8 +239,8 @@ class Redis(object):
def parse_response(self, connection, command_name, **options):
"Parses a response from the Redis server"
response = connection.read_response()
- if command_name in self.RESPONSE_CALLBACKS:
- return self.RESPONSE_CALLBACKS[command_name](response, **options)
+ if command_name in self.response_callbacks:
+ return self.response_callbacks[command_name](response, **options)
return response
#### SERVER INFORMATION ####
@@ -1022,6 +1028,13 @@ class Redis(object):
class PubSub(object):
+ """
+ PubSub provides publish, subscribe and listen support to Redis channels.
+
+ After subscribing to one or more channels, the listen() method will block
+ until a message arrives on one of the subscribed channels. That message
+ will be returned and it's safe to start listening again.
+ """
def __init__(self, connection_pool, shard_hint=None):
self.connection_pool = connection_pool
self.shard_hint = shard_hint
@@ -1211,8 +1224,8 @@ class Pipeline(Redis):
if not isinstance(r, Exception):
args, options = cmd
command_name = args[0]
- if command_name in self.RESPONSE_CALLBACKS:
- r = self.RESPONSE_CALLBACKS[command_name](r, **options)
+ if command_name in self.response_callbacks:
+ r = self.response_callbacks[command_name](r, **options)
data.append(r)
return data
finally: