From a21987f68f3e94cc5e0746790a9c117fbdd86c86 Mon Sep 17 00:00:00 2001 From: Nickolai Novik Date: Sat, 21 Jun 2014 21:31:44 +0300 Subject: bitpos command implementation --- redis/client.py | 28 ++++++++++++++++++++++++---- tests/test_commands.py | 22 ++++++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/redis/client.py b/redis/client.py index 0ab8081..bffe226 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), @@ -782,6 +782,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/tests/test_commands.py b/tests/test_commands.py index aaff22e..11b15d2 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, '\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, '\x00\xff\xf0') + assert r.bitpos(key, 1, 0) == 8 + assert r.bitpos(key, 1, 1) == 8 + r.set(key, '\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, '\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') -- cgit v1.2.1 From b993b322556fc366e6369b7d80d054ada2a75d7f Mon Sep 17 00:00:00 2001 From: Nickolai Novik Date: Sat, 21 Jun 2014 21:52:10 +0300 Subject: fix tests --- tests/test_commands.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_commands.py b/tests/test_commands.py index 11b15d2..286ea04 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -233,20 +233,20 @@ class TestRedisCommands(object): @skip_if_server_version_lt('2.8.7') def test_bitpos(self, r): key = 'key:bitpos' - r.set(key, '\xff\xf0\x00') + 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, '\x00\xff\xf0') + r.set(key, b('\x00\xff\xf0')) assert r.bitpos(key, 1, 0) == 8 assert r.bitpos(key, 1, 1) == 8 - r.set(key, '\x00\x00\x00') + 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, '\xff\xf0\x00') + r.set(key, b('\xff\xf0\x00')) with pytest.raises(exceptions.RedisError): r.bitpos(key, 0, end=1) == 12 with pytest.raises(exceptions.RedisError): -- cgit v1.2.1 From 246db5b13c5b3c21263b9db42ebefaeb963d5bb9 Mon Sep 17 00:00:00 2001 From: Andy McCurdy Date: Tue, 17 Jun 2014 09:08:35 -0700 Subject: better pack_commands algorithm with less string joining --- redis/connection.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/redis/connection.py b/redis/connection.py index f9efa7b..141731d 100755 --- a/redis/connection.py +++ b/redis/connection.py @@ -600,19 +600,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 - - if buff: - pieces.append(buff) - return pieces + pieces.append(packed) + buffer_length += len(packed) + + if buffer_length > 6000: + output.append(SYM_EMPTY.join(pieces)) + buffer_length = 0 + pieces = [] + + if pieces: + output.append(SYM_EMPTY.join(pieces)) + return output class SSLConnection(Connection): -- cgit v1.2.1 From 310cb1c58c059fc6d007a67edda8166fb4c962e6 Mon Sep 17 00:00:00 2001 From: Andy McCurdy Date: Thu, 3 Jul 2014 21:51:58 -0700 Subject: Consistent option names for encoding and encoding_errors. Fixes #510 --- CHANGES | 7 +++++++ redis/client.py | 18 ++++++++++++++---- redis/connection.py | 11 +++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 32fce25..15f66f7 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,13 @@ 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. * 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..9271e68 100755 --- a/redis/client.py +++ b/redis/client.py @@ -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 } diff --git a/redis/connection.py b/redis/connection.py index 141731d..4c14233 100755 --- a/redis/connection.py +++ b/redis/connection.py @@ -783,6 +783,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, -- cgit v1.2.1 From 83013b5df1ea32213a77f81be08ffb40d56bace6 Mon Sep 17 00:00:00 2001 From: Andy McCurdy Date: Sun, 6 Jul 2014 19:52:28 -0700 Subject: check for the server closing a connection that's compatible with Python 3 fixes #508 --- CHANGES | 2 ++ redis/connection.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 15f66f7..d1c021d 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,8 @@ 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 sockets the server closes + a connection. * 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/connection.py b/redis/connection.py index 4c14233..e39bd39 100755 --- a/redis/connection.py +++ b/redis/connection.py @@ -108,7 +108,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) @@ -314,7 +314,7 @@ class HiredisParser(BaseParser): try: 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: raise TimeoutError("Timeout reading from socket") -- cgit v1.2.1 From 5bb604242fe258e70742416c9907ce087a713771 Mon Sep 17 00:00:00 2001 From: Andy McCurdy Date: Sun, 6 Jul 2014 20:04:27 -0700 Subject: changelog --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index d1c021d..271503e 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,7 @@ "encoding_errors" options. * Fixed a compatability bug with Python 3 when sockets 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 -- cgit v1.2.1 From 9da5664e9d4b1f95c9c4e01db765996fdbc4dba6 Mon Sep 17 00:00:00 2001 From: Andy McCurdy Date: Sun, 6 Jul 2014 21:55:38 -0700 Subject: typo --- CHANGES | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 271503e..27ff773 100644 --- a/CHANGES +++ b/CHANGES @@ -13,8 +13,8 @@ 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 sockets the server closes - a connection. + * 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 -- cgit v1.2.1