Skip to content

Commit

Permalink
Merge branch 'master' into pr/505
Browse files Browse the repository at this point in the history
Conflicts:
	redis/connection.py
  • Loading branch information
andymccurdy committed Jul 21, 2014
2 parents e03b828 + 9da5664 commit 49b438b
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 18 deletions.
10 changes: 10 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
46 changes: 38 additions & 8 deletions redis/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -392,18 +392,28 @@ def __init__(self, host='localhost', port=6379,
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
}
Expand Down Expand Up @@ -782,6 +792,26 @@ def bitop(self, operation, dest, *keys):
"""
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,
Expand Down
35 changes: 25 additions & 10 deletions redis/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def _read_from_socket(self, length=None):
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)
Expand Down Expand Up @@ -329,7 +329,7 @@ def read_response(self):
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:
Expand Down Expand Up @@ -623,19 +623,23 @@ def pack_command(self, *args):

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):
Expand Down Expand Up @@ -802,6 +806,17 @@ def from_url(cls, url, db=None, **kwargs):

# 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,
Expand Down
22 changes: 22 additions & 0 deletions tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,28 @@ def test_bitop_string_operands(self, r):
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')
Expand Down

0 comments on commit 49b438b

Please sign in to comment.