From d54b5ed527ff63fde23332c1ff938faf240d7a5a Mon Sep 17 00:00:00 2001 From: Daniel M Date: Fri, 8 Sep 2023 15:12:42 -0400 Subject: [PATCH] Fix stream key type (#234) --- docs/about/changelog.md | 22 +++++++++++-------- fakeredis/_basefakesocket.py | 25 ++++++++++++++++++++-- fakeredis/_commands.py | 20 +---------------- fakeredis/commands_mixins/generic_mixin.py | 4 ++-- pyproject.toml | 2 +- test/test_scan.py | 7 ++++++ 6 files changed, 47 insertions(+), 33 deletions(-) diff --git a/docs/about/changelog.md b/docs/about/changelog.md index 3844ae02..d3f5938d 100644 --- a/docs/about/changelog.md +++ b/docs/about/changelog.md @@ -7,6 +7,10 @@ description: Change log of all fakeredis releases ## v2.18.1 +### 🐛 Bug Fixes + +- Fix stream type issue #233 + ### 🧰 Maintenance - Add mypy hints to everything @@ -19,7 +23,7 @@ description: Change log of all fakeredis releases - Implement `PUBSUB NUMPAT` #195, `SSUBSCRIBE` #199, `SPUBLISH` #198, `SUNSUBSCRIBE` #200, `PUBSUB SHARDCHANNELS` #196, `PUBSUB SHARDNUMSUB` #197 -### 🧰 Bug Fixes +### 🐛 Bug Fixes - Fix All aio.FakeRedis instances share the same server #218 @@ -30,7 +34,7 @@ description: Change log of all fakeredis releases - Implement `LPOS` #207, `LMPOP` #184, and `BLMPOP` #183 - Implement `ZMPOP` #191, `BZMPOP` #186 -### 🧰 Bug Fixes +### 🐛 Bug Fixes - Fix incorrect error msg for the group not found #210 - fix: use the same server_key within the pipeline when issued watch #213 @@ -48,7 +52,7 @@ We'd like to thank all the contributors who worked on this release! - Implemented support for `JSON.MSET` #174, `JSON.MERGE` #181 -### 🧰 Bug Fixes +### 🐛 Bug Fixes - Add support for `version` for async FakeRedis #205 @@ -76,13 +80,13 @@ We'd like to thank all the contributors who worked on this release! ## v2.14.2 -### 🧰 Bug Fixes +### 🐛 Bug Fixes - Fix documentation link ## v2.14.1 -### 🧰 Bug Fixes +### 🐛 Bug Fixes - Fix requirement for packaging.Version #177 @@ -99,7 +103,7 @@ We'd like to thank all the contributors who worked on this release! ## v2.13.0 -### 🧰 Bug Fixes +### 🐛 Bug Fixes - Fixed xadd timestamp (fixes #151) (#152) - Implement XDEL #153 @@ -111,7 +115,7 @@ We'd like to thank all the contributors who worked on this release! ## v2.12.1 -### 🧰 Bug Fixes +### 🐛 Bug Fixes - Add support for `Connection.read_response` arguments used in redis-py 4.5.5 and 5.0.0 - Adding state for scan commands (#99) @@ -129,7 +133,7 @@ We'd like to thank all the contributors who worked on this release! ## v2.11.2 -### 🧰 Bug Fixes +### 🐛 Bug Fixes - Unique FakeServer when no connection params are provided (#142) @@ -148,7 +152,7 @@ We'd like to thank all the contributors who worked on this release! Creating multiple clients with the same connection parameters will result in the same server data structure. -### 🧰 Bug Fixes +### 🐛 Bug Fixes - Fix creating fakeredis.aioredis using url with user/password (#139) diff --git a/fakeredis/_basefakesocket.py b/fakeredis/_basefakesocket.py index 944ccbca..06684e65 100644 --- a/fakeredis/_basefakesocket.py +++ b/fakeredis/_basefakesocket.py @@ -9,7 +9,7 @@ from . import _msgs as msgs from ._command_args_parsing import extract_args -from ._commands import Int, Float, SUPPORTED_COMMANDS, COMMANDS_WITH_SUB, key_value_type +from ._commands import Int, Float, SUPPORTED_COMMANDS, COMMANDS_WITH_SUB, Item from ._helpers import ( SimpleError, valid_response_type, @@ -20,6 +20,8 @@ QUEUED, encode_command, ) +from ._stream import XStream +from ._zset import ZSet def _extract_command(fields: List[bytes]) -> Tuple[Any, List[Any]]: @@ -346,7 +348,7 @@ def match_key(key: bytes) -> Union[bool, Match[bytes], None]: return regex.match(key) if regex is not None else True def match_type(key) -> bool: - return _type is None or casematch(key_value_type(self._db[key]).value, _type) + return _type is None or casematch(BaseFakeSocket._key_value_type(self._db[key]).value, _type) if pattern is not None or _type is not None: for val in itertools.islice(data, cursor, cursor + count): @@ -377,3 +379,22 @@ def _encodeint(self, value: int) -> bytes: if self.version >= (7,): value = 0 + value return Int.encode(value) + + @staticmethod + def _key_value_type(key: Item) -> SimpleString: + if key.value is None: + return SimpleString(b"none") + elif isinstance(key.value, bytes): + return SimpleString(b"string") + elif isinstance(key.value, list): + return SimpleString(b"list") + elif isinstance(key.value, set): + return SimpleString(b"set") + elif isinstance(key.value, ZSet): + return SimpleString(b"zset") + elif isinstance(key.value, dict): + return SimpleString(b"hash") + elif isinstance(key.value, XStream): + return SimpleString(b"stream") + else: + assert False # pragma: nocover diff --git a/fakeredis/_commands.py b/fakeredis/_commands.py index 4c86cc66..01f5cd22 100644 --- a/fakeredis/_commands.py +++ b/fakeredis/_commands.py @@ -8,8 +8,7 @@ from typing import Tuple, Union, Optional, Any, Type from . import _msgs as msgs -from ._helpers import null_terminate, SimpleError, SimpleString, Database -from ._zset import ZSet +from ._helpers import null_terminate, SimpleError, Database MAX_STRING_SIZE = 512 * 1024 * 1024 SUPPORTED_COMMANDS = dict() # Dictionary of supported commands name => Signature @@ -463,20 +462,3 @@ def fix_range_string(start: int, end: int, length: int) -> Tuple[int, int]: end = max(0, end + length) end = min(end, length - 1) return start, end + 1 - - -def key_value_type(key: Item) -> SimpleString: - if key.value is None: - return SimpleString(b"none") - elif isinstance(key.value, bytes): - return SimpleString(b"string") - elif isinstance(key.value, list): - return SimpleString(b"list") - elif isinstance(key.value, set): - return SimpleString(b"set") - elif isinstance(key.value, ZSet): - return SimpleString(b"zset") - elif isinstance(key.value, dict): - return SimpleString(b"hash") - else: - assert False # pragma: nocover diff --git a/fakeredis/commands_mixins/generic_mixin.py b/fakeredis/commands_mixins/generic_mixin.py index d5c91e49..9d780e08 100644 --- a/fakeredis/commands_mixins/generic_mixin.py +++ b/fakeredis/commands_mixins/generic_mixin.py @@ -14,7 +14,6 @@ CommandItem, SortFloat, delete_keys, - key_value_type, ) from fakeredis._helpers import compile_pattern, SimpleError, OK, casematch, Database from fakeredis._zset import ZSet @@ -27,6 +26,7 @@ class GenericCommandsMixin: _db_num: int _ttl: Callable _scan: Callable + _key_value_type: Callable def _lookup_key(self, key, pattern): """Python implementation of lookupKeyByPattern from redis""" @@ -297,7 +297,7 @@ def ttl(self, key): @command((Key(),)) def type(self, key): - return key_value_type(key) + return self._key_value_type(key) @command((Key(),), (Key(),), name="unlink") def unlink(self, *keys): diff --git a/pyproject.toml b/pyproject.toml index 0bcd7710..32c64e48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ name = "fakeredis" packages = [ { include = "fakeredis" }, ] -version = "2.18.0" +version = "2.18.1" description = "Python implementation of redis API, can be used for testing purposes." readme = "README.md" keywords = ["redis", "RedisJson", "tests", "redis-stack"] diff --git a/test/test_scan.py b/test/test_scan.py index a4a1c0e9..f7ee4f26 100644 --- a/test/test_scan.py +++ b/test/test_scan.py @@ -191,3 +191,10 @@ def test_scan_expired_key(r: redis.Redis): r.pexpire('expiringkey', 1) sleep(1) assert r.scan()[1] == [] + + +def test_scan_stream(r: redis.Redis): + r.xadd("mystream", {"test": "value"}) + assert r.type("mystream") == b"stream" + for s in r.scan_iter(_type="STRING"): + print(s)