Hello community,

here is the log from the commit of package python-redis for openSUSE:Factory 
checked in at 2012-02-14 11:26:39
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-redis (Old)
 and      /work/SRC/openSUSE:Factory/.python-redis.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-redis", Maintainer is ""

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-redis/python-redis.changes        
2011-11-28 18:27:14.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.python-redis.new/python-redis.changes   
2012-02-14 11:26:40.000000000 +0100
@@ -1,0 +2,34 @@
+Tue Feb  7 01:15:02 UTC 2012 - [email protected]
+
+- Update to 2.4.11:
+  * AuthenticationError will now be correctly raised if an invalid password
+    is supplied.
+  * If Hiredis is unavailable, the HiredisParser will raise a RedisError
+    if selected manually.
+  * Made the INFO command more tolerant of Redis changes formatting. Fix
+    for #217.
+- Aditional changes from 2.4.10:
+  * Buffer reads from socket in the PythonParser. Fix for a Windows-specific
+    bug (#205).
+  * Added the OBJECT and DEBUG OBJECT commands.
+  * Added __del__ methods for classes that hold on to resources that need to
+    be cleaned up. This should prevent resource leakage when these objects
+    leave scope due to misuse or unhandled exceptions. Thanks David Wolever
+    for the suggestion.
+  * Added the ECHO command for completeness.
+  * Fixed a bug where attempting to subscribe to a PubSub channel of a Redis
+    server that's down would blow out the stack. Fixes #179 and #195. Thanks
+    Ovidiu Predescu for the test case.
+  * StrictRedis's TTL command now returns a -1 when querying a key with no
+    expiration. The Redis class continues to return None.
+  * ZADD and SADD now return integer values indicating the number of items
+    added. Thanks Homer Strong.
+  * Renamed the base client class to StrictRedis, replacing ZADD and LREM in
+    favor of their official argument order. The Redis class is now a subclass
+    of StrictRedis, implementing the legacy redis-py implementations of ZADD
+    and LREM. Docs have been updated to suggesting the use of StrictRedis.
+  * SETEX in StrictRedis is now compliant with official Redis SETEX command.
+    the name, value, time implementation moved to "Redis" for backwards
+    compatability.
+
+-------------------------------------------------------------------

Old:
----
  redis-2.4.9.tar.gz

New:
----
  redis-2.4.11.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-redis.spec ++++++
--- /var/tmp/diff_new_pack.KgAlRS/_old  2012-02-14 11:26:40.000000000 +0100
+++ /var/tmp/diff_new_pack.KgAlRS/_new  2012-02-14 11:26:40.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-redis
 #
-# Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany.
+# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -11,13 +11,12 @@
 # case the license is the MIT License). An "Open Source License" is a
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
-#
+
 # Please submit bugfixes or comments via http://bugs.opensuse.org/
 #
 
-
 Name:           python-redis
-Version:        2.4.9
+Version:        2.4.11
 Release:        0
 Url:            http://github.com/andymccurdy/redis-py
 Summary:        Python client for Redis key-value store

++++++ redis-2.4.9.tar.gz -> redis-2.4.11.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redis-2.4.9/CHANGES new/redis-2.4.11/CHANGES
--- old/redis-2.4.9/CHANGES     2011-07-23 01:25:23.000000000 +0200
+++ new/redis-2.4.11/CHANGES    2012-01-13 22:47:27.000000000 +0100
@@ -1,3 +1,33 @@
+* 2.4.11
+    * AuthenticationError will now be correctly raised if an invalid password
+      is supplied.
+    * If Hiredis is unavailable, the HiredisParser will raise a RedisError
+      if selected manually.
+    * Made the INFO command more tolerant of Redis changes formatting. Fix
+      for #217.
+* 2.4.10
+    * Buffer reads from socket in the PythonParser. Fix for a Windows-specific
+      bug (#205).
+    * Added the OBJECT and DEBUG OBJECT commands.
+    * Added __del__ methods for classes that hold on to resources that need to
+      be cleaned up. This should prevent resource leakage when these objects
+      leave scope due to misuse or unhandled exceptions. Thanks David Wolever
+      for the suggestion.
+    * Added the ECHO command for completeness.
+    * Fixed a bug where attempting to subscribe to a PubSub channel of a Redis
+      server that's down would blow out the stack. Fixes #179 and #195. Thanks
+      Ovidiu Predescu for the test case.
+    * StrictRedis's TTL command now returns a -1 when querying a key with no
+      expiration. The Redis class continues to return None.
+    * ZADD and SADD now return integer values indicating the number of items
+      added. Thanks Homer Strong.
+    * Renamed the base client class to StrictRedis, replacing ZADD and LREM in
+      favor of their official argument order. The Redis class is now a subclass
+      of StrictRedis, implementing the legacy redis-py implementations of ZADD
+      and LREM. Docs have been updated to suggesting the use of StrictRedis.
+    * SETEX in StrictRedis is now compliant with official Redis SETEX command.
+      the name, value, time implementation moved to "Redis" for backwards
+      compatability.
 * 2.4.9
     * Removed socket retry logic in Connection. This is the responsbility of
       the caller to determine if the command is safe and can be retried. Thanks
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redis-2.4.9/PKG-INFO new/redis-2.4.11/PKG-INFO
--- old/redis-2.4.9/PKG-INFO    2011-07-23 01:26:05.000000000 +0200
+++ new/redis-2.4.11/PKG-INFO   2012-01-13 22:48:58.000000000 +0100
@@ -1,12 +1,12 @@
 Metadata-Version: 1.0
 Name: redis
-Version: 2.4.9
+Version: 2.4.11
 Summary: Python client for Redis key-value store
 Home-page: http://github.com/andymccurdy/redis-py
 Author: Andy McCurdy
 Author-email: [email protected]
 License: MIT
-Download-URL: 
http://cloud.github.com/downloads/andymccurdy/redis-py/redis-2.4.9.tar.gz
+Download-URL: 
http://cloud.github.com/downloads/andymccurdy/redis-py/redis-2.4.11.tar.gz
 Description: # redis-py
         
         The Python interface to the Redis key-value store.
@@ -27,12 +27,47 @@
         ## Getting Started
         
         >>> import redis
-        >>> r = redis.Redis(host='localhost', port=6379, db=0)
+        >>> r = redis.StrictRedis(host='localhost', port=6379, db=0)
         >>> r.set('foo', 'bar')
         True
         >>> r.get('foo')
         'bar'
         
+        ## API Reference
+        
+        The official Redis documentation does a great job of explaining each 
command in
+        detail (http://redis.io/commands). redis-py exposes two client classes 
that
+        implement these commands. The StrictRedis class attempts to adhere to 
the
+        official official command syntax. There are a few exceptions:
+        
+        * SELECT: Not implemented. See the explanation in the Thread Safety 
section
+        below.
+        * DEL: 'del' is a reserved keyword in the Python syntax. Therefore 
redis-py
+        uses 'delete' instead.
+        * CONFIG GET|SET: These are implemented separately as 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
+        pipeline to be wrapped with the MULTI and EXEC statements when it is 
executed.
+        See more about Pipelines below.
+        * 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.
+        
+        In addition to the changes above, the Redis class, a subclass of 
StrictRedis,
+        overrides several other commands to provide backwards compatibility 
with older
+        versions of redis-py:
+        
+        * LREM: Order of 'num' and 'value' arguments reversed such that 'num' 
can
+        provide a default value of zero.
+        * 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. The Redis class expects *args in the form of:
+        name1, score1, name2, score2, ...
+        * SETEX: Order of 'time' and 'value' arguments reversed.
+        
+        
         ## More Detail
         
         ### Connection Pools
@@ -48,7 +83,7 @@
         >>> pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
         >>> r = redis.Redis(connection_pool=pool)
         
-        ### Connetions
+        ### Connections
         
         ConnectionPools manage a set of Connection instances. redis-py ships 
with two
         types of Connections. The default, Connection, is a normal TCP socket 
based
@@ -131,7 +166,7 @@
         create a separate client instance (and possibly a separate connection 
pool) for
         each database.
         
-        It is not save to pass PubSub objects between threads.
+        It is not safe to pass PubSub or Pipeline objects between threads.
         
         ## Pipelines
         
@@ -145,7 +180,7 @@
         >>> r = redis.Redis(...)
         >>> r.set('bing', 'baz')
         >>> # Use the pipeline() method to create a pipeline instance
-        >>> pipe = redis.pipeline()
+        >>> pipe = r.pipeline()
         >>> # The following SET commands are buffered
         >>> pipe.set('foo', 'bar')
         >>> pipe.get('bing')
@@ -179,7 +214,7 @@
         
         Enter the WATCH command. WATCH provides the ability to monitor one or 
more keys
         prior to starting a transaction. If any of those keys change prior the
-        execution of that transaction, the entre transaction will be canceled 
and a
+        execution of that transaction, the entire transaction will be canceled 
and a
         WatchError will be raised. To implement our own client-side INCR 
command, we
         could do something like this:
         
@@ -208,10 +243,10 @@
         ...             continue
         
         Note that, because the Pipeline must bind to a single connection for 
the
-        duration of a WATCH, care must be taken to ensure that he connection is
+        duration of a WATCH, care must be taken to ensure that the connection 
is
         returned to the connection pool by calling the reset() method. If the
         Pipeline is used as a context manager (as in the example above) reset()
-        will be called automatically. Of course you can do this the manual way 
as by
+        will be called automatically. Of course you can do this the manual way 
by
         explicity calling reset():
         
         >>> pipe = r.pipeline()
@@ -241,35 +276,6 @@
         >>> r.transaction(client_side_incr, 'OUR-SEQUENCE-KEY')
         [True]
         
-        
-        ## 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 as 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
-        pipeline to be wrapped with the MULTI and EXEC statements when it is 
executed.
-        See more about Pipelines above.
-        * 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.
-        * LREM: Order of 'num' and 'value' arguments reversed such that 'num' 
can
-        provide a default value of zero.
-        
         ## Versioning scheme
         
         redis-py is versioned after Redis. For example, redis-py 2.0.0 should
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redis-2.4.9/README.md new/redis-2.4.11/README.md
--- old/redis-2.4.9/README.md   2011-07-18 00:48:52.000000000 +0200
+++ new/redis-2.4.11/README.md  2011-11-08 01:50:42.000000000 +0100
@@ -18,12 +18,47 @@
 ## Getting Started
 
     >>> import redis
-    >>> r = redis.Redis(host='localhost', port=6379, db=0)
+    >>> r = redis.StrictRedis(host='localhost', port=6379, db=0)
     >>> r.set('foo', 'bar')
     True
     >>> r.get('foo')
     'bar'
 
+## API Reference
+
+The official Redis documentation does a great job of explaining each command in
+detail (http://redis.io/commands). redis-py exposes two client classes that
+implement these commands. The StrictRedis class attempts to adhere to the
+official official command syntax. There are a few exceptions:
+
+* SELECT: Not implemented. See the explanation in the Thread Safety section
+  below.
+* DEL: 'del' is a reserved keyword in the Python syntax. Therefore redis-py
+  uses 'delete' instead.
+* CONFIG GET|SET: These are implemented separately as 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
+  pipeline to be wrapped with the MULTI and EXEC statements when it is 
executed.
+  See more about Pipelines below.
+* 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.
+
+In addition to the changes above, the Redis class, a subclass of StrictRedis,
+overrides several other commands to provide backwards compatibility with older
+versions of redis-py:
+
+* LREM: Order of 'num' and 'value' arguments reversed such that 'num' can
+  provide a default value of zero.
+* 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. The Redis class expects *args in the form of:
+      name1, score1, name2, score2, ...
+* SETEX: Order of 'time' and 'value' arguments reversed.
+
+
 ## More Detail
 
 ### Connection Pools
@@ -39,7 +74,7 @@
     >>> pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
     >>> r = redis.Redis(connection_pool=pool)
 
-### Connetions
+### Connections
 
 ConnectionPools manage a set of Connection instances. redis-py ships with two
 types of Connections. The default, Connection, is a normal TCP socket based
@@ -122,7 +157,7 @@
 create a separate client instance (and possibly a separate connection pool) for
 each database.
 
-It is not save to pass PubSub objects between threads.
+It is not safe to pass PubSub or Pipeline objects between threads.
 
 ## Pipelines
 
@@ -136,7 +171,7 @@
     >>> r = redis.Redis(...)
     >>> r.set('bing', 'baz')
     >>> # Use the pipeline() method to create a pipeline instance
-    >>> pipe = redis.pipeline()
+    >>> pipe = r.pipeline()
     >>> # The following SET commands are buffered
     >>> pipe.set('foo', 'bar')
     >>> pipe.get('bing')
@@ -170,7 +205,7 @@
 
 Enter the WATCH command. WATCH provides the ability to monitor one or more keys
 prior to starting a transaction. If any of those keys change prior the
-execution of that transaction, the entre transaction will be canceled and a
+execution of that transaction, the entire transaction will be canceled and a
 WatchError will be raised. To implement our own client-side INCR command, we
 could do something like this:
 
@@ -199,10 +234,10 @@
     ...             continue
 
 Note that, because the Pipeline must bind to a single connection for the
-duration of a WATCH, care must be taken to ensure that he connection is
+duration of a WATCH, care must be taken to ensure that the connection is
 returned to the connection pool by calling the reset() method. If the
 Pipeline is used as a context manager (as in the example above) reset()
-will be called automatically. Of course you can do this the manual way as by
+will be called automatically. Of course you can do this the manual way by
 explicity calling reset():
 
     >>> pipe = r.pipeline()
@@ -232,35 +267,6 @@
     >>> r.transaction(client_side_incr, 'OUR-SEQUENCE-KEY')
     [True]
 
-
-## 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 as 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
-  pipeline to be wrapped with the MULTI and EXEC statements when it is 
executed.
-  See more about Pipelines above.
-* 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.
-* LREM: Order of 'num' and 'value' arguments reversed such that 'num' can
-  provide a default value of zero.
-
 ## Versioning scheme
 
 redis-py is versioned after Redis. For example, redis-py 2.0.0 should
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redis-2.4.9/redis/__init__.py 
new/redis-2.4.11/redis/__init__.py
--- old/redis-2.4.9/redis/__init__.py   2011-07-23 01:25:34.000000000 +0200
+++ new/redis-2.4.11/redis/__init__.py  2012-01-13 22:48:06.000000000 +0100
@@ -1,4 +1,4 @@
-from redis.client import Redis
+from redis.client import Redis, StrictRedis
 from redis.connection import (
     ConnectionPool,
     Connection,
@@ -16,11 +16,12 @@
     )
 
 
-__version__ = '2.4.9'
+__version__ = '2.4.11'
 VERSION = tuple(map(int, __version__.split('.')))
 
 __all__ = [
-    'Redis', 'ConnectionPool', 'Connection', 'UnixDomainSocketConnection',
+    'Redis', 'StrictRedis', 'ConnectionPool',
+    'Connection', 'UnixDomainSocketConnection',
     'RedisError', 'ConnectionError', 'ResponseError', 'AuthenticationError',
     'InvalidResponse', 'DataError', 'PubSubError', 'WatchError',
     ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redis-2.4.9/redis/client.py 
new/redis-2.4.11/redis/client.py
--- old/redis-2.4.9/redis/client.py     2011-07-18 17:10:52.000000000 +0200
+++ new/redis-2.4.11/redis/client.py    2012-01-13 22:45:24.000000000 +0100
@@ -44,12 +44,36 @@
     [merged.update(d) for d in dicts]
     return merged
 
+def parse_debug_object(response):
+    "Parse the results of Redis's DEBUG OBJECT command into a Python dict"
+    # The 'type' of the object is the first item in the response, but isn't
+    # prefixed with a name
+    response = 'type:' + response
+    response = dict([kv.split(':') for kv in response.split()])
+
+    # parse some expected int values from the string response
+    # note: this cmd isn't spec'd so these may not appear in all redis versions
+    int_fields = ('refcount', 'serializedlength', 'lru', 'lru_seconds_idle')
+    for field in int_fields:
+        if field in response:
+            response[field] = int(response[field])
+
+    return response
+
+def parse_object(response, infotype):
+    "Parse the results of an OBJECT command"
+    if infotype in ('idletime', 'refcount'):
+        return int(response)
+    return response
+
 def parse_info(response):
     "Parse the result of Redis's INFO command into a Python dict"
     info = {}
+
     def get_value(value):
-        if ',' not in value:
+        if ',' not in value or '=' not in value:
             return value
+
         sub_dict = {}
         for item in value.split(','):
             k, v = item.rsplit('=', 1)
@@ -58,6 +82,7 @@
             except ValueError:
                 sub_dict[k] = v
         return sub_dict
+
     for line in response.splitlines():
         if line and not line.startswith('#'):
             key, value = line.split(':')
@@ -102,7 +127,7 @@
         return response and pairs_to_dict(response) or {}
     return response == 'OK'
 
-class Redis(object):
+class StrictRedis(object):
     """
     Implementation of the Redis protocol.
 
@@ -115,13 +140,13 @@
     RESPONSE_CALLBACKS = dict_merge(
         string_keys_to_dict(
             'AUTH DEL EXISTS EXPIRE EXPIREAT HDEL HEXISTS HMSET MOVE MSETNX '
-            'PERSIST RENAMENX SADD SISMEMBER SMOVE SETEX SETNX SREM ZADD ZREM',
+            'PERSIST RENAMENX SISMEMBER SMOVE SETEX SETNX SREM ZREM',
             bool
             ),
         string_keys_to_dict(
-            'DECRBY GETBIT HLEN INCRBY LINSERT LLEN LPUSHX RPUSHX SCARD '
-            'SDIFFSTORE SETBIT SETRANGE SINTERSTORE STRLEN SUNIONSTORE ZCARD '
-            'ZREMRANGEBYRANK ZREMRANGEBYSCORE',
+            'DECRBY GETBIT HLEN INCRBY LINSERT LLEN LPUSHX RPUSHX SADD SCARD '
+            'SDIFFSTORE SETBIT SETRANGE SINTERSTORE STRLEN SUNIONSTORE ZADD '
+            'ZCARD ZREMRANGEBYRANK ZREMRANGEBYSCORE',
             int
             ),
         string_keys_to_dict(
@@ -149,12 +174,13 @@
             'BGSAVE': lambda r: r == 'Background saving started',
             'BRPOPLPUSH': lambda r: r and r or None,
             'CONFIG': parse_config,
+            'DEBUG': parse_debug_object,
             'HGETALL': lambda r: r and pairs_to_dict(r) or {},
             'INFO': parse_info,
             'LASTSAVE': timestamp_to_datetime,
+            'OBJECT': parse_object,
             'PING': lambda r: r == 'PONG',
             'RANDOMKEY': lambda r: r and r or None,
-            'TTL': lambda r: r != -1 and r or None,
         }
         )
 
@@ -198,7 +224,7 @@
         atomic, pipelines are useful for reducing the back-and-forth overhead
         between the client and server.
         """
-        return Pipeline(
+        return StrictPipeline(
             self.connection_pool,
             self.response_callbacks,
             transaction,
@@ -289,11 +315,19 @@
         "Returns the number of keys in the current database"
         return self.execute_command('DBSIZE')
 
+    def debug_object(self, key):
+        "Returns version specific metainformation about a give key"
+        return self.execute_command('DEBUG', 'OBJECT', key)
+
     def delete(self, *names):
         "Delete one or more keys specified by ``names``"
         return self.execute_command('DEL', *names)
     __delitem__ = delete
 
+    def echo(self, value):
+        "Echo the string back from the server"
+        return self.execute_command('ECHO', value)
+
     def flushall(self):
         "Delete all keys in all databases on the current host"
         return self.execute_command('FLUSHALL')
@@ -313,6 +347,10 @@
         """
         return self.execute_command('LASTSAVE')
 
+    def object(self, infotype, key):
+        "Return the encoding, idletime, or refcount about the key"
+        return self.execute_command('OBJECT', infotype, key, infotype=infotype)
+
     def ping(self):
         "Ping the Redis server"
         return self.execute_command('PING')
@@ -474,7 +512,7 @@
         value = value and 1 or 0
         return self.execute_command('SETBIT', name, offset, value)
 
-    def setex(self, name, value, time):
+    def setex(self, name, time, value):
         """
         Set the value of key ``name`` to ``value``
         that expires in ``time`` seconds
@@ -628,13 +666,17 @@
         """
         return self.execute_command('LRANGE', name, start, end)
 
-    def lrem(self, name, value, num=0):
+    def lrem(self, name, count, value):
         """
-        Remove the first ``num`` occurrences of ``value`` from list ``name``
+        Remove the first ``count`` occurrences of elements equal to ``value``
+        from the list stored at ``name``.
 
-        If ``num`` is 0, then all occurrences will be removed
+        The count argument influences the operation in the following ways:
+            count > 0: Remove elements equal to value moving from head to tail.
+            count < 0: Remove elements equal to value moving from tail to head.
+            count = 0: Remove all elements equal to value.
         """
-        return self.execute_command('LREM', name, num, value)
+        return self.execute_command('LREM', name, count, value)
 
     def lset(self, name, index, value):
         "Set ``position`` of list ``name`` to ``value``"
@@ -798,27 +840,27 @@
 
 
     #### SORTED SET COMMANDS ####
-    def zadd(self, name, value=None, score=None, **pairs):
+    def zadd(self, name, *args, **kwargs):
         """
-        For each kwarg in ``pairs``, add that item and it's score to the
-        sorted set ``name``.
+        Set any number of score, element-name pairs to the key ``name``. Pairs
+        can be specified in two ways:
 
-        The ``value`` and ``score`` arguments are deprecated.
-        """
-        all_pairs = []
-        if value is not None or score is not None:
-            if value is None or score is None:
-                raise RedisError("Both 'value' and 'score' must be specified " 
\
-                                 "to ZADD")
-            warnings.warn(DeprecationWarning(
-                "Passing 'value' and 'score' has been deprecated. " \
-                "Please pass via kwargs instead."))
-            all_pairs.append(score)
-            all_pairs.append(value)
-        for pair in pairs.iteritems():
-            all_pairs.append(pair[1])
-            all_pairs.append(pair[0])
-        return self.execute_command('ZADD', name, *all_pairs)
+        As *args, in the form of: score1, name1, score2, name2, ...
+        or as **kwargs, in the form of: name1=score1, name2=score2, ...
+
+        The following example would add four values to the 'my-key' key:
+        redis.zadd('my-key', 1.1, 'name1', 2.2, 'name2', name3=3.3, name4=4.4)
+        """
+        pieces = []
+        if args:
+            if len(args) % 2 != 0:
+                raise RedisError("ZADD requires an equal number of "
+                                 "values and scores")
+            pieces.extend(args)
+        for pair in kwargs.iteritems():
+            pieces.append(pair[1])
+            pieces.append(pair[0])
+        return self.execute_command('ZADD', name, *pieces)
 
     def zcard(self, name):
         "Return the number of elements in the sorted set ``name``"
@@ -1063,6 +1105,86 @@
         return self.execute_command('PUBLISH', channel, message)
 
 
+class Redis(StrictRedis):
+    """
+    Provides backwards compatibility with older versions of redis-py that
+    changed arguments to some commands to be more Pythonic, sane, or by
+    accident.
+    """
+
+    # Overridden callbacks
+    RESPONSE_CALLBACKS = dict_merge(
+        StrictRedis.RESPONSE_CALLBACKS,
+        {
+            'TTL': lambda r: r != -1 and r or None,
+        }
+    )
+
+    def pipeline(self, transaction=True, shard_hint=None):
+        """
+        Return a new pipeline object that can queue multiple commands for
+        later execution. ``transaction`` indicates whether all commands
+        should be executed atomically. Apart from making a group of operations
+        atomic, pipelines are useful for reducing the back-and-forth overhead
+        between the client and server.
+        """
+        return Pipeline(
+            self.connection_pool,
+            self.response_callbacks,
+            transaction,
+            shard_hint)
+
+    def setex(self, name, value, time):
+        """
+        Set the value of key ``name`` to ``value``
+        that expires in ``time`` seconds
+        """
+        return self.execute_command('SETEX', name, time, value)
+
+    def lrem(self, name, value, num=0):
+        """
+        Remove the first ``num`` occurrences of elements equal to ``value``
+        from the list stored at ``name``.
+
+        The ``num`` argument influences the operation in the following ways:
+            num > 0: Remove elements equal to value moving from head to tail.
+            num < 0: Remove elements equal to value moving from tail to head.
+            num = 0: Remove all elements equal to value.
+        """
+        return self.execute_command('LREM', name, num, value)
+
+    def zadd(self, name, *args, **kwargs):
+        """
+        NOTE: The order of arguments differs from that of the official ZADD
+        command. For backwards compatability, this method accepts arguments
+        in the form of name1, score1, name2, score2, while the official Redis
+        documents expects score1, name1, score2, name2.
+
+        If you're looking to use the standard syntax, consider using the
+        StrictRedis class. See the API Reference section of the docs for more
+        information.
+
+        Set any number of element-name, score pairs to the key ``name``. Pairs
+        can be specified in two ways:
+
+        As *args, in the form of: name1, score1, name2, score2, ...
+        or as **kwargs, in the form of: name1=score1, name2=score2, ...
+
+        The following example would add four values to the 'my-key' key:
+        redis.zadd('my-key', 'name1', 1.1, 'name2', 2.2, name3=3.3, name4=4.4)
+        """
+        pieces = []
+        if args:
+            if len(args) % 2 != 0:
+                raise RedisError("ZADD requires an equal number of "
+                                 "values and scores")
+            pieces.extend(reversed(args))
+        for pair in kwargs.iteritems():
+            pieces.append(pair[1])
+            pieces.append(pair[0])
+        return self.execute_command('ZADD', name, *pieces)
+
+
 class PubSub(object):
     """
     PubSub provides publish, subscribe and listen support to Redis channels.
@@ -1082,6 +1204,22 @@
             ('subscribe', 'psubscribe', 'unsubscribe', 'punsubscribe')
             )
 
+    def __del__(self):
+        try:
+            # if this object went out of scope prior to shutting down
+            # subscriptions, close the connection manually before
+            # returning it to the connection pool
+            if self.connection and (self.channels or self.patterns):
+                self.connection.disconnect()
+            self.reset()
+        except:
+            pass
+
+    def reset(self):
+        if self.connection:
+            self.connection_pool.release(self.connection)
+            self.connection = None
+
     def execute_command(self, *args, **kwargs):
         "Execute a publish/subscribe command"
         if self.connection is None:
@@ -1095,6 +1233,9 @@
             return self.parse_response()
         except ConnectionError:
             connection.disconnect()
+            # Connect manually here. If the Redis server is down, this will
+            # fail and raise a ConnectionError as desired.
+            connection.connect()
             # resubscribe to all channels and patterns before
             # resending the current command
             for channel in self.channels:
@@ -1112,8 +1253,7 @@
             # if we've just unsubscribed from the remaining channels,
             # release the connection back to the pool
             if not self.subscription_count:
-                self.connection_pool.release(self.connection)
-                self.connection = None
+                self.reset()
         return response
 
     def psubscribe(self, patterns):
@@ -1181,7 +1321,7 @@
             yield msg
 
 
-class Pipeline(Redis):
+class BasePipeline(object):
     """
     Pipelines provide a way to transmit multiple commands to the Redis server
     in one transmission.  This is convenient for batch processing, such as
@@ -1219,6 +1359,12 @@
     def __exit__(self, exc_type, exc_value, traceback):
         self.reset()
 
+    def __del__(self):
+        try:
+            self.reset()
+        except:
+            pass
+
     def reset(self):
         self.command_stack = []
         # make sure to reset the connection state in the event that we were
@@ -1341,8 +1487,8 @@
                 for args, options in commands]
 
     def parse_response(self, connection, command_name, **options):
-        result = super(Pipeline, self).parse_response(
-            connection, command_name, **options)
+        result = StrictRedis.parse_response(
+            self, connection, command_name, **options)
         if command_name in self.UNWATCH_COMMANDS:
             self.watching = False
         elif command_name == 'WATCH':
@@ -1353,7 +1499,7 @@
         "Execute all the commands in the current pipeline"
         stack = self.command_stack
         if self.transaction or self.explicit_transaction:
-            stack = [(('MULTI' ,), {})] + stack + [(('EXEC', ), {})]
+            stack = [(('MULTI', ), {})] + stack + [(('EXEC', ), {})]
             execute = self._execute_transaction
         else:
             execute = self._execute_pipeline
@@ -1398,6 +1544,14 @@
         return self.watching and self.execute_command('UNWATCH') or True
 
 
+class StrictPipeline(BasePipeline, StrictRedis):
+    "Pipeline for the StrictRedis class"
+    pass
+
+class Pipeline(BasePipeline, Redis):
+    "Pipeline for the Redis class"
+    pass
+
 class LockError(RedisError):
     "Errors thrown from the Lock"
     pass
@@ -1426,8 +1580,8 @@
         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.
+        that are running clients have their time synchronized with a network 
time
+        service like ntp.
         """
         self.redis = redis
         self.name = name
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redis-2.4.9/redis/connection.py 
new/redis-2.4.11/redis/connection.py
--- old/redis-2.4.9/redis/connection.py 2011-07-23 01:22:49.000000000 +0200
+++ new/redis-2.4.11/redis/connection.py        2011-11-08 01:57:26.000000000 
+0100
@@ -1,12 +1,37 @@
-import errno
 import socket
 from itertools import chain, imap
-from redis.exceptions import ConnectionError, ResponseError, InvalidResponse
+from redis.exceptions import (
+    RedisError,
+    ConnectionError,
+    ResponseError,
+    InvalidResponse,
+    AuthenticationError
+)
+
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from StringIO import StringIO
+
+try:
+    import hiredis
+    hiredis_available = True
+except ImportError:
+    hiredis_available = False
 
 class PythonParser(object):
+    "Plain Python parsing class"
+    MAX_READ_LENGTH = 1000000
+
     def __init__(self):
         self._fp = None
 
+    def __del__(self):
+        try:
+            self.on_disconnect()
+        except:
+            pass
+
     def on_connect(self, connection):
         "Called when the socket connects"
         self._fp = connection._sock.makefile('r')
@@ -24,7 +49,25 @@
         """
         try:
             if length is not None:
-                return self._fp.read(length+2)[:-2]
+                bytes_left = length + 2 # read the line ending
+                if length > self.MAX_READ_LENGTH:
+                    # apparently reading more than 1MB or so from a windows
+                    # socket can cause MemoryErrors. See:
+                    # https://github.com/andymccurdy/redis-py/issues/205
+                    # read smaller chunks at a time to work around this
+                    try:
+                        buf = StringIO()
+                        while bytes_left > 0:
+                            read_len = min(bytes_left, self.MAX_READ_LENGTH)
+                            buf.write(self._fp.read(read_len))
+                            bytes_left -= read_len
+                        buf.seek(0)
+                        return buf.read(length)
+                    finally:
+                        buf.close()
+                return self._fp.read(bytes_left)[:-2]
+
+            # no length, read a full line
             return self._fp.readline()[:-2]
         except (socket.error, socket.timeout), e:
             raise ConnectionError("Error while reading from socket: %s" % \
@@ -68,6 +111,17 @@
         raise InvalidResponse("Protocol Error")
 
 class HiredisParser(object):
+    "Parser class for connections using Hiredis"
+    def __init__(self):
+        if not hiredis_available:
+            raise RedisError("Hiredis is not installed")
+
+    def __del__(self):
+        try:
+            self.on_disconnect()
+        except:
+            pass
+
     def on_connect(self, connection):
         self._sock = connection._sock
         self._reader = hiredis.Reader(
@@ -79,6 +133,8 @@
         self._reader = None
 
     def read_response(self):
+        if not self._reader:
+            raise ConnectionError("Socket closed on remote end")
         response = self._reader.gets()
         while response is False:
             try:
@@ -96,12 +152,12 @@
             response = self._reader.gets()
         return response
 
-try:
-    import hiredis
+if hiredis_available:
     DefaultParser = HiredisParser
-except ImportError:
+else:
     DefaultParser = PythonParser
 
+
 class Connection(object):
     "Manages TCP communication to and from a Redis server"
     def __init__(self, host='localhost', port=6379, db=0, password=None,
@@ -117,6 +173,12 @@
         self._sock = None
         self._parser = parser_class()
 
+    def __del__(self):
+        try:
+            self.disconnect()
+        except:
+            pass
+
     def connect(self):
         "Connects to the Redis server if not already connected"
         if self._sock:
@@ -146,7 +208,6 @@
             return "Error %s connecting %s:%s. %s." % \
                 (exception.args[0], self.host, self.port, exception.args[1])
 
-
     def on_connect(self):
         "Initialize the connection, authenticate and select a database"
         self._parser.on_connect(self)
@@ -155,7 +216,7 @@
         if self.password:
             self.send_command('AUTH', self.password)
             if self.read_response() != 'OK':
-                raise ConnectionError('Invalid Password')
+                raise AuthenticationError('Invalid Password')
 
         # if a database is specified, switch to it
         if self.db:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redis-2.4.9/redis.egg-info/PKG-INFO 
new/redis-2.4.11/redis.egg-info/PKG-INFO
--- old/redis-2.4.9/redis.egg-info/PKG-INFO     2011-07-23 01:26:05.000000000 
+0200
+++ new/redis-2.4.11/redis.egg-info/PKG-INFO    2012-01-13 22:48:58.000000000 
+0100
@@ -1,12 +1,12 @@
 Metadata-Version: 1.0
 Name: redis
-Version: 2.4.9
+Version: 2.4.11
 Summary: Python client for Redis key-value store
 Home-page: http://github.com/andymccurdy/redis-py
 Author: Andy McCurdy
 Author-email: [email protected]
 License: MIT
-Download-URL: 
http://cloud.github.com/downloads/andymccurdy/redis-py/redis-2.4.9.tar.gz
+Download-URL: 
http://cloud.github.com/downloads/andymccurdy/redis-py/redis-2.4.11.tar.gz
 Description: # redis-py
         
         The Python interface to the Redis key-value store.
@@ -27,12 +27,47 @@
         ## Getting Started
         
         >>> import redis
-        >>> r = redis.Redis(host='localhost', port=6379, db=0)
+        >>> r = redis.StrictRedis(host='localhost', port=6379, db=0)
         >>> r.set('foo', 'bar')
         True
         >>> r.get('foo')
         'bar'
         
+        ## API Reference
+        
+        The official Redis documentation does a great job of explaining each 
command in
+        detail (http://redis.io/commands). redis-py exposes two client classes 
that
+        implement these commands. The StrictRedis class attempts to adhere to 
the
+        official official command syntax. There are a few exceptions:
+        
+        * SELECT: Not implemented. See the explanation in the Thread Safety 
section
+        below.
+        * DEL: 'del' is a reserved keyword in the Python syntax. Therefore 
redis-py
+        uses 'delete' instead.
+        * CONFIG GET|SET: These are implemented separately as 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
+        pipeline to be wrapped with the MULTI and EXEC statements when it is 
executed.
+        See more about Pipelines below.
+        * 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.
+        
+        In addition to the changes above, the Redis class, a subclass of 
StrictRedis,
+        overrides several other commands to provide backwards compatibility 
with older
+        versions of redis-py:
+        
+        * LREM: Order of 'num' and 'value' arguments reversed such that 'num' 
can
+        provide a default value of zero.
+        * 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. The Redis class expects *args in the form of:
+        name1, score1, name2, score2, ...
+        * SETEX: Order of 'time' and 'value' arguments reversed.
+        
+        
         ## More Detail
         
         ### Connection Pools
@@ -48,7 +83,7 @@
         >>> pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
         >>> r = redis.Redis(connection_pool=pool)
         
-        ### Connetions
+        ### Connections
         
         ConnectionPools manage a set of Connection instances. redis-py ships 
with two
         types of Connections. The default, Connection, is a normal TCP socket 
based
@@ -131,7 +166,7 @@
         create a separate client instance (and possibly a separate connection 
pool) for
         each database.
         
-        It is not save to pass PubSub objects between threads.
+        It is not safe to pass PubSub or Pipeline objects between threads.
         
         ## Pipelines
         
@@ -145,7 +180,7 @@
         >>> r = redis.Redis(...)
         >>> r.set('bing', 'baz')
         >>> # Use the pipeline() method to create a pipeline instance
-        >>> pipe = redis.pipeline()
+        >>> pipe = r.pipeline()
         >>> # The following SET commands are buffered
         >>> pipe.set('foo', 'bar')
         >>> pipe.get('bing')
@@ -179,7 +214,7 @@
         
         Enter the WATCH command. WATCH provides the ability to monitor one or 
more keys
         prior to starting a transaction. If any of those keys change prior the
-        execution of that transaction, the entre transaction will be canceled 
and a
+        execution of that transaction, the entire transaction will be canceled 
and a
         WatchError will be raised. To implement our own client-side INCR 
command, we
         could do something like this:
         
@@ -208,10 +243,10 @@
         ...             continue
         
         Note that, because the Pipeline must bind to a single connection for 
the
-        duration of a WATCH, care must be taken to ensure that he connection is
+        duration of a WATCH, care must be taken to ensure that the connection 
is
         returned to the connection pool by calling the reset() method. If the
         Pipeline is used as a context manager (as in the example above) reset()
-        will be called automatically. Of course you can do this the manual way 
as by
+        will be called automatically. Of course you can do this the manual way 
by
         explicity calling reset():
         
         >>> pipe = r.pipeline()
@@ -241,35 +276,6 @@
         >>> r.transaction(client_side_incr, 'OUR-SEQUENCE-KEY')
         [True]
         
-        
-        ## 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 as 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
-        pipeline to be wrapped with the MULTI and EXEC statements when it is 
executed.
-        See more about Pipelines above.
-        * 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.
-        * LREM: Order of 'num' and 'value' arguments reversed such that 'num' 
can
-        provide a default value of zero.
-        
         ## Versioning scheme
         
         redis-py is versioned after Redis. For example, redis-py 2.0.0 should

-- 
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to