diff options
author | Andy McCurdy <andy@andymccurdy.com> | 2014-07-21 10:54:09 -0700 |
---|---|---|
committer | Andy McCurdy <andy@andymccurdy.com> | 2014-07-21 10:54:09 -0700 |
commit | 49b438bf149919011f2e08e031f329c570f9ed15 (patch) | |
tree | 49fdde7e8e59229f8e51d6af8048d029e050dfd4 | |
parent | e03b828f083b50d46db03d5c6bf443c1fe2bfcba (diff) | |
parent | 9da5664e9d4b1f95c9c4e01db765996fdbc4dba6 (diff) | |
download | redis-py-49b438bf149919011f2e08e031f329c570f9ed15.tar.gz |
Merge branch 'master' into pr/505
Conflicts:
redis/connection.py
-rw-r--r-- | CHANGES | 10 | ||||
-rwxr-xr-x | redis/client.py | 46 | ||||
-rwxr-xr-x | redis/connection.py | 35 | ||||
-rw-r--r-- | tests/test_commands.py | 22 |
4 files changed, 95 insertions, 18 deletions
@@ -6,6 +6,16 @@ another thread to release it, you need to disable thread local storage. Refer to the doc strings on the Lock class about the thread_local argument information. + * Fixed a regression in from_url where "charset" and "errors" weren't + valid options. "encoding" and "encoding_errors" are still accepted + and preferred. + * The "charset" and "errors" options have been deprecated. Passing + either to StrictRedis.__init__ or from_url will still work but will + also emit a DeprecationWarning. Instead use the "encoding" and + "encoding_errors" options. + * Fixed a compatability bug with Python 3 when the server closes a + connection. + * Added BITPOS command. Thanks https://github.com/jettify. * 2.10.1 * Fixed a bug where Sentinel connections to a server that's no longer a master and receives a READONLY error will disconnect and reconnect to diff --git a/redis/client.py b/redis/client.py index 0ab8081..74cca86 100755 --- a/redis/client.py +++ b/redis/client.py @@ -293,10 +293,10 @@ class StrictRedis(object): bool ), string_keys_to_dict( - 'BITCOUNT DECRBY DEL GETBIT HDEL HLEN INCRBY LINSERT LLEN LPUSHX ' - 'PFADD PFCOUNT RPUSHX SADD SCARD SDIFFSTORE SETBIT SETRANGE ' - 'SINTERSTORE SREM STRLEN SUNIONSTORE ZADD ZCARD ZLEXCOUNT ZREM ' - 'ZREMRANGEBYLEX ZREMRANGEBYRANK ZREMRANGEBYSCORE', + 'BITCOUNT BITPOS DECRBY DEL GETBIT HDEL HLEN INCRBY LINSERT LLEN ' + 'LPUSHX PFADD PFCOUNT RPUSHX SADD SCARD SDIFFSTORE SETBIT ' + 'SETRANGE SINTERSTORE SREM STRLEN SUNIONSTORE ZADD ZCARD ' + 'ZLEXCOUNT ZREM ZREMRANGEBYLEX ZREMRANGEBYRANK ZREMRANGEBYSCORE', int ), string_keys_to_dict('INCRBYFLOAT HINCRBYFLOAT', float), @@ -392,18 +392,28 @@ class StrictRedis(object): db=0, password=None, socket_timeout=None, socket_connect_timeout=None, socket_keepalive=None, socket_keepalive_options=None, - connection_pool=None, charset='utf-8', errors='strict', + connection_pool=None, unix_socket_path=None, + encoding='utf-8', encoding_errors='strict', + charset=None, errors=None, decode_responses=False, retry_on_timeout=False, - unix_socket_path=None, ssl=False, ssl_keyfile=None, ssl_certfile=None, ssl_cert_reqs=None, ssl_ca_certs=None): if not connection_pool: + if charset is not None: + warnings.warn(DeprecationWarning( + '"charset" is deprecated. Use "encoding" instead')) + encoding = charset + if errors is not None: + warnings.warn(DeprecationWarning( + '"errors" is deprecated. Use "encoding_errors" instead')) + encoding_errors = errors + kwargs = { 'db': db, 'password': password, 'socket_timeout': socket_timeout, - 'encoding': charset, - 'encoding_errors': errors, + 'encoding': encoding, + 'encoding_errors': encoding_errors, 'decode_responses': decode_responses, 'retry_on_timeout': retry_on_timeout } @@ -782,6 +792,26 @@ class StrictRedis(object): """ return self.execute_command('BITOP', operation, dest, *keys) + def bitpos(self, key, bit, start=None, end=None): + """ + Return the position of the first bit set to 1 or 0 in a string. + ``start`` and ``end`` difines search range. The range is interpreted + as a range of bytes and not a range of bits, so start=0 and end=2 + means to look at the first three bytes. + """ + if bit not in (0, 1): + raise RedisError('bit must be 0 or 1') + params = [key, bit] + + start is not None and params.append(start) + + if start is not None and end is not None: + params.append(end) + elif start is None and end is not None: + raise RedisError("start argument is not set, " + "when end is specified") + return self.execute_command('BITPOS', *params) + def decr(self, name, amount=1): """ Decrements the value of ``key`` by ``amount``. If no key exists, diff --git a/redis/connection.py b/redis/connection.py index 7018051..864a9f8 100755 --- a/redis/connection.py +++ b/redis/connection.py @@ -110,7 +110,7 @@ class SocketBuffer(object): while True: data = self._sock.recv(socket_read_size) # an empty string indicates the server shutdown the socket - if isinstance(data, str) and len(data) == 0: + if isinstance(data, bytes) and len(data) == 0: raise socket.error("Connection closed by remote server.") buf.write(data) data_length = len(data) @@ -329,7 +329,7 @@ server.") else: buffer = self._sock.recv(socket_read_size) # an empty string indicates the server shutdown the socket - if isinstance(buffer, str) and len(buffer) == 0: + if isinstance(buffer, bytes) and len(buffer) == 0: raise socket.error("Connection closed by remote \ server.") except socket.timeout: @@ -623,19 +623,23 @@ class Connection(object): def pack_commands(self, commands): "Pack multiple commands into the Redis protocol" + output = [] pieces = [] - buff = SYM_EMPTY + buffer_length = 0 for cmd in commands: packed = self.pack_command(*cmd)[0] - buff = SYM_EMPTY.join((buff, packed)) - if len(buff) > 6000: - pieces.append(buff) - buff = SYM_EMPTY + pieces.append(packed) + buffer_length += len(packed) + + if buffer_length > 6000: + output.append(SYM_EMPTY.join(pieces)) + buffer_length = 0 + pieces = [] - if buff: - pieces.append(buff) - return pieces + if pieces: + output.append(SYM_EMPTY.join(pieces)) + return output class SSLConnection(Connection): @@ -802,6 +806,17 @@ class ConnectionPool(object): # update the arguments from the URL values kwargs.update(url_options) + + # backwards compatability + if 'charset' in kwargs: + warnings.warn(DeprecationWarning( + '"charset" is deprecated. Use "encoding" instead')) + kwargs['encoding'] = kwargs.pop('charset') + if 'errors' in kwargs: + warnings.warn(DeprecationWarning( + '"errors" is deprecated. Use "encoding_errors" instead')) + kwargs['encoding_errors'] = kwargs.pop('errors') + return cls(**kwargs) def __init__(self, connection_class=Connection, max_connections=None, diff --git a/tests/test_commands.py b/tests/test_commands.py index aaff22e..286ea04 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -230,6 +230,28 @@ class TestRedisCommands(object): assert int(binascii.hexlify(r['res2']), 16) == 0x0102FFFF assert int(binascii.hexlify(r['res3']), 16) == 0x000000FF + @skip_if_server_version_lt('2.8.7') + def test_bitpos(self, r): + key = 'key:bitpos' + r.set(key, b('\xff\xf0\x00')) + assert r.bitpos(key, 0) == 12 + assert r.bitpos(key, 0, 2, -1) == 16 + assert r.bitpos(key, 0, -2, -1) == 12 + r.set(key, b('\x00\xff\xf0')) + assert r.bitpos(key, 1, 0) == 8 + assert r.bitpos(key, 1, 1) == 8 + r.set(key, b('\x00\x00\x00')) + assert r.bitpos(key, 1) == -1 + + @skip_if_server_version_lt('2.8.7') + def test_bitpos_wrong_arguments(self, r): + key = 'key:bitpos:wrong:args' + r.set(key, b('\xff\xf0\x00')) + with pytest.raises(exceptions.RedisError): + r.bitpos(key, 0, end=1) == 12 + with pytest.raises(exceptions.RedisError): + r.bitpos(key, 7) == 12 + def test_decr(self, r): assert r.decr('a') == -1 assert r['a'] == b('-1') |