diff --git a/.circleci/config.yml b/.circleci/config.yml index f0315e5..947c2ec 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -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 diff --git a/README.md b/README.md index 8c76354..e43190d 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/redis_namespace/__init__.py b/redis_namespace/__init__.py index 5ef8598..e42815c 100644 --- a/redis_namespace/__init__.py +++ b/redis_namespace/__init__.py @@ -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 = { @@ -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]) @@ -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)) @@ -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 @@ -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] diff --git a/redis_namespace/_version.py b/redis_namespace/_version.py index e8fa884..7c63057 100644 --- a/redis_namespace/_version.py +++ b/redis_namespace/_version.py @@ -1,3 +1,3 @@ """Version information.""" -__version__ = "3.5.3.1" +__version__ = "4.5.5.1" diff --git a/requirements.txt b/requirements.txt index 3fc0632..54a7b14 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -redis==3.5.3 +redis==4.5.5 diff --git a/tests/test_commands.py b/tests/test_commands.py index 0238fa0..e145d42 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1,4 +1,5 @@ from __future__ import unicode_literals +from string import ascii_letters import binascii import datetime import pytest @@ -6,10 +7,8 @@ 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 @@ -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) @@ -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) @@ -485,7 +484,7 @@ 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): @@ -493,7 +492,7 @@ def test_msetnx(self, r): 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 @@ -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)) @@ -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) @@ -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') @@ -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): diff --git a/tests/test_encoding.py b/tests/test_encoding.py index dd9d516..525a43d 100644 --- a/tests/test_encoding.py +++ b/tests/test_encoding.py @@ -3,7 +3,6 @@ import redis import redis_namespace -from redis._compat import unichr, unicode from .conftest import _get_client @@ -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 diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py index 06abb9f..16af827 100644 --- a/tests/test_pipeline.py +++ b/tests/test_pipeline.py @@ -2,7 +2,6 @@ import pytest import redis -from redis._compat import unichr, unicode class TestPipeline(object): @@ -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 @@ -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 @@ -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) @@ -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' diff --git a/tests/test_pubsub.py b/tests/test_pubsub.py index 6e1bc2a..f9c5ac5 100644 --- a/tests/test_pubsub.py +++ b/tests/test_pubsub.py @@ -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 @@ -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 } @@ -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 { @@ -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 @@ -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 @@ -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 @@ -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 { diff --git a/tests/test_sentinel.py b/tests/test_sentinel.py index 1081e2b..b2c35fc 100644 --- a/tests/test_sentinel.py +++ b/tests/test_sentinel.py @@ -3,7 +3,6 @@ from redis import exceptions from redis.sentinel import (Sentinel, SentinelConnectionPool, MasterNotFoundError, SlaveNotFoundError) -from redis._compat import next import redis.sentinel