Skip to content

[IP-6943] enhance redis-namespace to work with redis-py 4.5.x and Redis 5.0.x #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ jobs:
install-and-test:
docker:
- image: cimg/python:3.11
- image: circleci/redis:5.0.0
- image: cimg/redis:5.0
steps:
- checkout
- run: pip install -r requirements.txt
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ redis_connection.get('ns:foo')

Supported list

| redis-py | redis-namespace |
| --- | --- |
| 3.5.3 | 3.5.3.1 |
| 3.0.1 | 3.0.1.1 |
| 2.10.6 | 2.10.6.1 |
| 2.10.5 | 2.10.5.2 |
| 2.10.3 | 2.10.3.1 |
| redis-py | redis-namespace |
|----------|-----------------|
| 4.5.5 | 4.5.5.1 |
| 3.5.3 | 3.5.3.1 |
| 3.0.1 | 3.0.1.1 |
| 2.10.6 | 2.10.6.1 |
| 2.10.5 | 2.10.5.2 |
| 2.10.3 | 2.10.3.1 |


[travis-url]: https://travis-ci.org/guokr/redis-namespace
Expand Down
12 changes: 7 additions & 5 deletions redis_namespace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
from redis.client import Pipeline as _Pipeline, PubSub as _PubSub, EMPTY_RESPONSE
from redis.connection import ConnectionPool
from redis.exceptions import ResponseError
from redis._compat import nativestr, basestring


nativestr = lambda x: x if isinstance(x, str) else x.decode('utf-8', 'replace')


NAMESPACED_COMMANDS = {
Expand Down Expand Up @@ -252,7 +254,7 @@ def args_with_namespace(ns, *original_args):
is_custom_match = False
for i, a in enumerate(args):
if (
isinstance(a, basestring) and str(a).lower() == 'match'
isinstance(a, str) and str(a).lower() == 'match'
or isinstance(a, bytes) and a.decode('utf-8').lower() == 'match'
):
args[i+1] = add_namespace(ns, args[i + 1])
Expand Down Expand Up @@ -286,7 +288,7 @@ def add_namespace(ns, key):
return [add_namespace(ns, k) for k in key]
elif isinstance(key, dict):
return {add_namespace(ns, k): v for k, v in key.items()}
elif isinstance(key, basestring):
elif isinstance(key, str):
return '{}{}'.format(ns, key)
elif isinstance(key, bytes):
return '{}{}'.format(ns, nativestr(key))
Expand All @@ -300,7 +302,7 @@ def rm_namespace(ns, key):
return [rm_namespace(ns, k) for k in key]
elif isinstance(key, dict):
return {rm_namespace(ns, k): v for k, v in key.items()}
elif isinstance(key, (basestring, bytes)):
elif isinstance(key, (str, bytes)):
return key[len(ns):]
return key

Expand Down Expand Up @@ -348,7 +350,7 @@ def sort(self, name, start=None, num=None, by=None, get=None,
args = [name, by, store]
name, by, store = add_namespace(self._namespace, args)
if get:
if isinstance(get, (basestring, bytes)):
if isinstance(get, (str, bytes)):
get = add_namespace(self._namespace, get)
elif isinstance(get, (list, tuple)):
get = [add_namespace(self._namespace, i) if i != '#' else i for i in get]
Expand Down
2 changes: 1 addition & 1 deletion redis_namespace/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Version information."""

__version__ = "3.5.3.1"
__version__ = "4.5.5.1"
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
redis==3.5.3
redis==4.5.5
27 changes: 13 additions & 14 deletions tests/test_commands.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
from __future__ import unicode_literals
from string import ascii_letters
import binascii
import datetime
import pytest
import re
import redis
import time

from redis._compat import (unichr, ascii_letters, iteritems, iterkeys,
itervalues, long)
from redis.client import parse_info
from redis import exceptions
from redis.client import parse_info

from .conftest import skip_if_server_version_lt, skip_if_server_version_gte, with_bns

Expand Down Expand Up @@ -161,7 +160,7 @@ def test_ping(self, r):

def test_slowlog_get(self, r, slowlog, ns):
assert r.slowlog_reset()
unicode_string = unichr(3456) + 'abcd' + unichr(3421)
unicode_string = chr(3456) + 'abcd' + chr(3421)
r.get(unicode_string)
slowlog = r.slowlog_get()
assert isinstance(slowlog, list)
Expand Down Expand Up @@ -398,7 +397,7 @@ def test_get_and_set(self, r):
assert r.get('a') is None
byte_string = b'value'
integer = 5
unicode_string = unichr(3456) + 'abcd' + unichr(3421)
unicode_string = chr(3456) + 'abcd' + chr(3421)
assert r.set('byte_string', byte_string)
assert r.set('integer', 5)
assert r.set('unicode_string', unicode_string)
Expand Down Expand Up @@ -485,15 +484,15 @@ def test_mget(self, r):
def test_mset(self, r):
d = {'a': b'1', 'b': b'2', 'c': b'3'}
assert r.mset(d)
for k, v in iteritems(d):
for k, v in d.items():
assert r[k] == v

def test_msetnx(self, r):
d = {'a': b'1', 'b': b'2', 'c': b'3'}
assert r.msetnx(d)
d2 = {'a': b'x', 'd': b'4'}
assert not r.msetnx(d2)
for k, v in iteritems(d):
for k, v in d.items():
assert r[k] == v
assert r.get('d') is None

Expand Down Expand Up @@ -1369,7 +1368,7 @@ def test_hincrbyfloat(self, r):
def test_hkeys(self, r):
h = {b'a1': b'1', b'a2': b'2', b'a3': b'3'}
r.hmset('a', h)
local_keys = list(iterkeys(h))
local_keys = list(h.keys())
remote_keys = r.hkeys('a')
assert (sorted(local_keys) == sorted(remote_keys))

Expand All @@ -1396,7 +1395,7 @@ def test_hsetnx(self, r):
def test_hvals(self, r):
h = {b'a1': b'1', b'a2': b'2', b'a3': b'3'}
r.hmset('a', h)
local_vals = list(itervalues(h))
local_vals = list(h.values())
remote_vals = r.hvals('a')
assert sorted(local_vals) == sorted(remote_vals)

Expand Down Expand Up @@ -1949,8 +1948,8 @@ def test_xinfo_consumers(self, r):
]

# we can't determine the idle time, so just make sure it's an int
assert isinstance(info[0].pop('idle'), (int, long))
assert isinstance(info[1].pop('idle'), (int, long))
assert isinstance(info[0].pop('idle'), int)
assert isinstance(info[1].pop('idle'), int)
assert info == expected

@skip_if_server_version_lt('5.0.0')
Expand Down Expand Up @@ -2297,14 +2296,14 @@ def test_binary_lists(self, r):
b'foo\tbar\x07': [b'7', b'8', b'9'],
}
# fill in lists
for key, value in iteritems(mapping):
for key, value in mapping.items():
r.rpush(key, *value)

# check that KEYS returns all the keys as they are
assert sorted(r.keys('*')) == sorted(list(iterkeys(mapping)))
assert sorted(r.keys('*')) == sorted(list(mapping.keys()))

# check that it is possible to get list content by key name
for key, value in iteritems(mapping):
for key, value in mapping.items():
assert r.lrange(key, 0, -1) == value

def test_22_info(self, r):
Expand Down
7 changes: 3 additions & 4 deletions tests/test_encoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import redis
import redis_namespace

from redis._compat import unichr, unicode
from .conftest import _get_client


Expand All @@ -13,14 +12,14 @@ def r(self, request):
return _get_client(redis_namespace.Redis, request=request, decode_responses=True)

def test_simple_encoding(self, r):
unicode_string = unichr(3456) + 'abcd' + unichr(3421)
unicode_string = chr(3456) + 'abcd' + chr(3421)
r['unicode-string'] = unicode_string
cached_val = r['unicode-string']
assert isinstance(cached_val, unicode)
assert isinstance(cached_val, str)
assert unicode_string == cached_val

def test_list_encoding(self, r):
unicode_string = unichr(3456) + 'abcd' + unichr(3421)
unicode_string = chr(3456) + 'abcd' + chr(3421)
result = [unicode_string, unicode_string, unicode_string]
r.rpush('a', *result)
assert r.lrange('a', 0, -1) == result
Expand Down
13 changes: 6 additions & 7 deletions tests/test_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import pytest

import redis
from redis._compat import unichr, unicode


class TestPipeline(object):
Expand Down Expand Up @@ -111,7 +110,7 @@ def test_exec_error_raised(self, r, ns):
pipe.set('a', 1).set('b', 2).lpush('c', 3).set('d', 4)
with pytest.raises(redis.ResponseError) as ex:
pipe.execute()
assert unicode(ex.value).startswith('Command # 3 (LPUSH %sc 3) of '
assert str(ex.value).startswith('Command # 3 (LPUSH %sc 3) of '
'pipeline caused error: ' % ns)

# make sure the pipe was restored to a working state
Expand Down Expand Up @@ -153,7 +152,7 @@ def test_parse_error_raised(self, r, ns):
with pytest.raises(redis.ResponseError) as ex:
pipe.execute()

assert unicode(ex.value).startswith('Command # 2 (ZREM %sb) of '
assert str(ex.value).startswith('Command # 2 (ZREM %sb) of '
'pipeline caused error: ' % ns)

# make sure the pipe was restored to a working state
Expand Down Expand Up @@ -236,13 +235,13 @@ def test_exec_error_in_no_transaction_pipeline(self, r, ns):
with pytest.raises(redis.ResponseError) as ex:
pipe.execute()

assert unicode(ex.value).startswith('Command # 1 (LLEN %sa) of '
assert str(ex.value).startswith('Command # 1 (LLEN %sa) of '
'pipeline caused error: ' % ns)

assert r['a'] == b'1'

def test_exec_error_in_no_transaction_pipeline_unicode_command(self, r, ns):
key = unichr(3456) + 'abcd' + unichr(3421)
key = chr(3456) + 'abcd' + chr(3421)
r[key] = 1
with r.pipeline(transaction=False) as pipe:
pipe.llen(key)
Expand All @@ -251,9 +250,9 @@ def test_exec_error_in_no_transaction_pipeline_unicode_command(self, r, ns):
with pytest.raises(redis.ResponseError) as ex:
pipe.execute()

expected = unicode('Command # 1 (LLEN %s%s) of pipeline caused '
expected = str('Command # 1 (LLEN %s%s) of pipeline caused '
'error: ') % (ns, key)
assert unicode(ex.value).startswith(expected)
assert str(ex.value).startswith(expected)

assert r[key] == b'1'

Expand Down
19 changes: 9 additions & 10 deletions tests/test_pubsub.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import redis
from redis.exceptions import ConnectionError
from redis._compat import basestring, unichr
import redis_namespace

from .conftest import _get_client
Expand All @@ -29,7 +28,7 @@ def make_message(type, channel, data, pattern=None):
'type': type,
'pattern': pattern and pattern.encode('utf-8') or None,
'channel': channel and channel.encode('utf-8') or None,
'data': data.encode('utf-8') if isinstance(data, basestring) else data
'data': data.encode('utf-8') if isinstance(data, str) else data
}


Expand All @@ -41,7 +40,7 @@ def make_subscribe_test_data(pubsub, type):
'unsub_type': 'unsubscribe',
'sub_func': pubsub.subscribe,
'unsub_func': pubsub.unsubscribe,
'keys': ['foo', 'bar', 'uni' + unichr(4456) + 'code']
'keys': ['foo', 'bar', 'uni' + chr(4456) + 'code']
}
elif type == 'pattern':
return {
Expand All @@ -50,7 +49,7 @@ def make_subscribe_test_data(pubsub, type):
'unsub_type': 'punsubscribe',
'sub_func': pubsub.psubscribe,
'unsub_func': pubsub.punsubscribe,
'keys': ['f*', 'b*', 'uni' + unichr(4456) + '*']
'keys': ['f*', 'b*', 'uni' + chr(4456) + '*']
}
assert False, 'invalid subscribe type: %s' % type

Expand Down Expand Up @@ -268,7 +267,7 @@ def test_pattern_message_handler(self, r):

def test_unicode_channel_message_handler(self, r):
p = r.pubsub(ignore_subscribe_messages=True)
channel = 'uni' + unichr(4456) + 'code'
channel = 'uni' + chr(4456) + 'code'
channels = {channel: self.message_handler}
p.subscribe(**channels)
assert r.publish(channel, 'test message') == 1
Expand All @@ -277,8 +276,8 @@ def test_unicode_channel_message_handler(self, r):

def test_unicode_pattern_message_handler(self, r):
p = r.pubsub(ignore_subscribe_messages=True)
pattern = 'uni' + unichr(4456) + '*'
channel = 'uni' + unichr(4456) + 'code'
pattern = 'uni' + chr(4456) + '*'
channel = 'uni' + chr(4456) + 'code'
p.psubscribe(**{pattern: self.message_handler})
assert r.publish(channel, 'test message') == 1
assert wait_for_message(p) is None
Expand All @@ -297,9 +296,9 @@ def test_get_message_without_subscribe(self, r):
class TestPubSubAutoDecoding(object):
"These tests only validate that we get unicode values back"

channel = 'uni' + unichr(4456) + 'code'
pattern = 'uni' + unichr(4456) + '*'
data = 'abc' + unichr(4458) + '123'
channel = 'uni' + chr(4456) + 'code'
pattern = 'uni' + chr(4456) + '*'
data = 'abc' + chr(4458) + '123'

def make_message(self, type, channel, data, pattern=None):
return {
Expand Down
1 change: 0 additions & 1 deletion tests/test_sentinel.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from redis import exceptions
from redis.sentinel import (Sentinel, SentinelConnectionPool,
MasterNotFoundError, SlaveNotFoundError)
from redis._compat import next
import redis.sentinel


Expand Down