diff --git a/python/python/glide/async_commands/standalone_commands.py b/python/python/glide/async_commands/standalone_commands.py index 8405e6add7..7c0cb3224d 100644 --- a/python/python/glide/async_commands/standalone_commands.py +++ b/python/python/glide/async_commands/standalone_commands.py @@ -12,7 +12,6 @@ _build_sort_args, ) from glide.async_commands.transaction import BaseTransaction, Transaction -from glide.async_commands.utils.utils import convert_bytes_to_string_dict from glide.constants import OK, TOK, TResult from glide.protobuf.redis_request_pb2 import RequestType @@ -50,7 +49,7 @@ async def info( Returns: - str: Returns a string containing the information for the sections requested. + bytes: Returns bytes containing the information for the sections requested. """ args = [section.value for section in sections] if sections else [] return cast(bytes, await self._execute_command(RequestType.Info, args)) diff --git a/python/python/glide/async_commands/utils/utils.py b/python/python/glide/async_commands/utils/utils.py deleted file mode 100644 index 88defe0061..0000000000 --- a/python/python/glide/async_commands/utils/utils.py +++ /dev/null @@ -1,61 +0,0 @@ -from typing import Any, Dict, Mapping, Optional, Union - -from glide.constants import TClusterDecodedResponse, TClusterResponse - - -def convert_bytes_to_string_dict( - # TODO: remove the str options - byte_string_dict: Optional[ - Union[Mapping[bytes, Any], Dict[bytes, Any], Mapping[str, Any], Dict[str, Any]] - ] -) -> Optional[Dict[str, Any]]: - """ - Recursively convert the keys and values of a dictionary from byte strings to regular strings, - handling nested dictionaries of any depth. - - Args: - byte_string_dict (Optional[Union[Mapping[bytes, Any], Dict[bytes, Any]]]): - A dictionary where keys and values can be byte strings or nested dictionaries. - - Returns: - Optional[Dict[str, Any]]: - A dictionary with keys and values converted to regular strings, or None if input is None. - """ - if byte_string_dict is None: - return None - - def convert(item: Any) -> Any: - if isinstance(item, dict): - return {convert(key): convert(value) for key, value in item.items()} - elif isinstance(item, bytes): - return item.decode("utf-8") - elif isinstance(item, list): - return [convert(elem) for elem in item] - else: - return item - - return convert(byte_string_dict) - - -def convert_bytes_to_string_cluster_response( - cluster_response: Optional[TClusterResponse], -) -> Optional[TClusterDecodedResponse]: - """ - Convert a TClusterResponse type with byte strings to a TClusterResponse type with regular strings, - handling nested dictionaries of any depth. - - Args: - cluster_response (Optional[Union[T, Dict[bytes, T]]]): - A cluster response which can be of type T or a dictionary with byte string keys and values. - - Returns: - Optional[TClusterResponse]: - A cluster response with all byte strings converted to regular strings. - """ - if cluster_response is None: - return None - - if isinstance(cluster_response, dict): - return convert_bytes_to_string_dict(cluster_response) - - return cluster_response diff --git a/python/python/glide/constants.py b/python/python/glide/constants.py index a527c06cfd..0eacccb040 100644 --- a/python/python/glide/constants.py +++ b/python/python/glide/constants.py @@ -30,7 +30,6 @@ # When routing to a single node, response will be T # Otherwise, response will be : {Address : response , ... } with type of Dict[str, T]. TClusterResponse = Union[T, Dict[bytes, T]] -TClusterDecodedResponse = Union[T, Dict[str, T]] TSingleNodeRoute = Union[RandomNode, SlotKeyRoute, SlotIdRoute, ByAddressRoute] # When specifying legacy path (path doesn't start with `$`), response will be T # Otherwise, (when specifying JSONPath), response will be List[Optional[T]]. diff --git a/python/python/glide/glide_client.py b/python/python/glide/glide_client.py index 57ea40cdfe..3a2c27c1d8 100644 --- a/python/python/glide/glide_client.py +++ b/python/python/glide/glide_client.py @@ -251,8 +251,8 @@ async def _execute_command( request.callback_idx = self._get_callback_index() request.single_command.request_type = request_type request.single_command.args_array.args[:] = [ - bytes(elem, encoding="utf8") for elem in args if isinstance(elem, str) - ] # TODO - use arg pointer + bytes(elem, encoding="utf8") if isinstance(elem, str) else elem for elem in args + ] (encoded_args, args_size) = self._encode_and_sum_size(args) if args_size < MAX_REQUEST_ARGS_LEN: request.single_command.args_array.args[:] = encoded_args diff --git a/python/python/tests/test_async_client.py b/python/python/tests/test_async_client.py index 69b482bfff..7da6c0dc9b 100644 --- a/python/python/tests/test_async_client.py +++ b/python/python/tests/test_async_client.py @@ -91,8 +91,8 @@ check_if_server_version_lt, compare_maps, convert_bytes_to_string_dict, - convert_str_to_bytes_list, - convert_str_to_bytes_set, + convert_string_to_bytes_dict, + convert_string_to_bytes_dict, generate_lua_lib_code, get_first_result, get_random_string, @@ -111,8 +111,6 @@ async def test_register_client_name_and_version(self, redis_client: TGlideClient # TODO: change it to pytest fixture after we'll implement a sync client return pytest.mark.skip(reason=f"Redis version required >= {min_version}") info = await redis_client.custom_command(["CLIENT", "INFO"]) - # We check bytes string because custom_command will always return so. - # To get String, we use redis client's info command. assert isinstance(info, bytes) info_str = info.decode() assert "lib-name=GlidePy" in info_str @@ -394,8 +392,7 @@ async def test_info_server_replication(self, redis_client: TGlideClient): cluster_mode = parse_info_response(info_res)["redis_mode"] expected_cluster_mode = isinstance(redis_client, GlideClusterClient) assert cluster_mode == "cluster" if expected_cluster_mode else "standalone" - info_res = get_first_result(await redis_client.info([InfoSection.REPLICATION])) - info = info_res.decode() + info = get_first_result(await redis_client.info([InfoSection.REPLICATION])).decode() assert "# Replication" in info assert "# Errorstats" not in info @@ -411,7 +408,7 @@ async def test_info_default(self, redis_client: TGlideClient): expected_num_of_results = cluster_nodes.count(b"master") assert len(info_result) == expected_num_of_results info_result = get_first_result(info_result) - assert "# Memory" in info_result.decode() + assert b"# Memory" in info_result @pytest.mark.parametrize("cluster_mode", [False]) @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) @@ -1043,10 +1040,10 @@ async def test_lpush_lpop_lrange(self, redis_client: TGlideClient): assert await redis_client.lpush(key, value_list) == 4 assert await redis_client.lpop(key) == value_list[-1].encode("utf-8") - assert await redis_client.lrange(key, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key, 0, -1) == convert_string_to_bytes_dict( value_list[-2::-1] ) - assert await redis_client.lpop_count(key, 2) == convert_str_to_bytes_list( + assert await redis_client.lpop_count(key, 2) == convert_string_to_bytes_dict( value_list[-2:0:-1] ) assert await redis_client.lrange("non_existing_key", 0, -1) == [] @@ -1084,7 +1081,7 @@ async def test_lpushx(self, redis_client: TGlideClient): # existing key assert await redis_client.lpush(key1, ["0"]) == 1 assert await redis_client.lpushx(key1, ["1", "2", "3"]) == 4 - assert await redis_client.lrange(key1, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key1, 0, -1) == convert_string_to_bytes_dict( ["3", "2", "1", "0"] ) # key exists, but not a list @@ -1105,7 +1102,7 @@ async def test_blpop(self, redis_client: TGlideClient): value_list = [value1, value2] assert await redis_client.lpush(key1, value_list) == 2 - assert await redis_client.blpop([key1, key2], 0.5) == convert_str_to_bytes_list( + assert await redis_client.blpop([key1, key2], 0.5) == convert_string_to_bytes_dict( [key1, value2] ) # ensure that command doesn't time out even if timeout > request timeout (250ms by default) @@ -1231,7 +1228,7 @@ async def test_rpush_rpop(self, redis_client: TGlideClient): assert await redis_client.rpush(key, value_list) == 4 assert await redis_client.rpop(key) == value_list[-1].encode("utf-8") - assert await redis_client.rpop_count(key, 2) == convert_str_to_bytes_list( + assert await redis_client.rpop_count(key, 2) == convert_string_to_bytes_dict( value_list[-2:0:-1] ) assert await redis_client.rpop("non_existing_key") is None @@ -1262,7 +1259,7 @@ async def test_rpushx(self, redis_client: TGlideClient): # existing key assert await redis_client.rpush(key1, ["0"]) == 1 assert await redis_client.rpushx(key1, ["1", "2", "3"]) == 4 - assert await redis_client.lrange(key1, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key1, 0, -1) == convert_string_to_bytes_dict( ["0", "1", "2", "3"] ) # key existing, but it is not a list @@ -1284,7 +1281,7 @@ async def test_brpop(self, redis_client: TGlideClient): assert await redis_client.lpush(key1, value_list) == 2 # ensure that command doesn't time out even if timeout > request timeout (250ms by default) - assert await redis_client.brpop([key1, key2], 0.5) == convert_str_to_bytes_list( + assert await redis_client.brpop([key1, key2], 0.5) == convert_string_to_bytes_dict( [key1, value1] ) @@ -1312,7 +1309,7 @@ async def test_linsert(self, redis_client: TGlideClient): assert await redis_client.lpush(key1, ["4", "3", "2", "1"]) == 4 assert await redis_client.linsert(key1, InsertPosition.BEFORE, "2", "1.5") == 5 assert await redis_client.linsert(key1, InsertPosition.AFTER, "3", "3.5") == 6 - assert await redis_client.lrange(key1, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key1, 0, -1) == convert_string_to_bytes_dict( [ "1", "1.5", @@ -1351,10 +1348,10 @@ async def test_lmove(self, redis_client: TGlideClient): await redis_client.lmove(key1, key2, ListDirection.LEFT, ListDirection.LEFT) == b"1" ) - assert await redis_client.lrange(key1, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key1, 0, -1) == convert_string_to_bytes_dict( ["2"] ) - assert await redis_client.lrange(key2, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key2, 0, -1) == convert_string_to_bytes_dict( ["1", "3", "4"] ) @@ -1366,7 +1363,7 @@ async def test_lmove(self, redis_client: TGlideClient): == b"2" ) assert await redis_client.lrange(key1, 0, -1) == [] - assert await redis_client.lrange(key2, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key2, 0, -1) == convert_string_to_bytes_dict( ["1", "3", "4", "2"] ) @@ -1377,10 +1374,10 @@ async def test_lmove(self, redis_client: TGlideClient): ) == b"2" ) - assert await redis_client.lrange(key2, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key2, 0, -1) == convert_string_to_bytes_dict( ["1", "3", "4"] ) - assert await redis_client.lrange(key1, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key1, 0, -1) == convert_string_to_bytes_dict( ["2"] ) @@ -1391,10 +1388,10 @@ async def test_lmove(self, redis_client: TGlideClient): ) == b"4" ) - assert await redis_client.lrange(key2, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key2, 0, -1) == convert_string_to_bytes_dict( ["1", "3"] ) - assert await redis_client.lrange(key1, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key1, 0, -1) == convert_string_to_bytes_dict( ["2", "4"] ) @@ -1436,10 +1433,10 @@ async def test_blmove(self, redis_client: TGlideClient): ) == b"1" ) - assert await redis_client.lrange(key1, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key1, 0, -1) == convert_string_to_bytes_dict( ["2"] ) - assert await redis_client.lrange(key2, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key2, 0, -1) == convert_string_to_bytes_dict( ["1", "3", "4"] ) @@ -1451,7 +1448,7 @@ async def test_blmove(self, redis_client: TGlideClient): == b"2" ) assert await redis_client.lrange(key1, 0, -1) == [] - assert await redis_client.lrange(key2, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key2, 0, -1) == convert_string_to_bytes_dict( ["1", "3", "4", "2"] ) @@ -1462,10 +1459,10 @@ async def test_blmove(self, redis_client: TGlideClient): ) == b"2" ) - assert await redis_client.lrange(key2, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key2, 0, -1) == convert_string_to_bytes_dict( ["1", "3", "4"] ) - assert await redis_client.lrange(key1, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key1, 0, -1) == convert_string_to_bytes_dict( ["2"] ) @@ -1476,10 +1473,10 @@ async def test_blmove(self, redis_client: TGlideClient): ) == b"4" ) - assert await redis_client.lrange(key2, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key2, 0, -1) == convert_string_to_bytes_dict( ["1", "3"] ) - assert await redis_client.lrange(key1, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key1, 0, -1) == convert_string_to_bytes_dict( ["2", "4"] ) @@ -1545,7 +1542,7 @@ async def test_lset(self, redis_client: TGlideClient): assert await redis_client.lset(key, 0, element) == OK values = [element] + values[:-1][::-1] - assert await redis_client.lrange(key, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key, 0, -1) == convert_string_to_bytes_dict( values ) @@ -1553,7 +1550,7 @@ async def test_lset(self, redis_client: TGlideClient): assert await redis_client.lset(key, -1, element) == OK values[-1] = element - assert await redis_client.lrange(key, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key, 0, -1) == convert_string_to_bytes_dict( values ) @@ -1567,7 +1564,7 @@ async def test_sadd_srem_smembers_scard(self, redis_client: TGlideClient): assert await redis_client.srem(key, ["member4", "nonExistingMember"]) == 1 assert set(await redis_client.smembers(key)) == set( - convert_str_to_bytes_list(value_list[:3]) + convert_string_to_bytes_dict(value_list[:3]) ) assert await redis_client.srem(key, ["member1"]) == 1 @@ -1628,7 +1625,7 @@ async def test_spop(self, redis_client: TGlideClient): member2 = get_random_string(5) member3 = get_random_string(5) assert await redis_client.sadd(key, [member, member2, member3]) == 3 - assert await redis_client.spop_count(key, 4) == convert_str_to_bytes_set( + assert await redis_client.spop_count(key, 4) == convert_string_to_bytes_dict( {member, member2, member3} ) @@ -1651,19 +1648,19 @@ async def test_smove(self, redis_client: TGlideClient): # move an element assert await redis_client.smove(key1, key2, "1") is True - assert await redis_client.smembers(key1) == convert_str_to_bytes_set({"2", "3"}) - assert await redis_client.smembers(key2) == convert_str_to_bytes_set( + assert await redis_client.smembers(key1) == convert_string_to_bytes_dict({"2", "3"}) + assert await redis_client.smembers(key2) == convert_string_to_bytes_dict( {"1", "2", "3"} ) # moved element already exists in the destination set assert await redis_client.smove(key2, key1, "2") is True - assert await redis_client.smembers(key1) == convert_str_to_bytes_set({"2", "3"}) - assert await redis_client.smembers(key2) == convert_str_to_bytes_set({"1", "3"}) + assert await redis_client.smembers(key1) == convert_string_to_bytes_dict({"2", "3"}) + assert await redis_client.smembers(key2) == convert_string_to_bytes_dict({"1", "3"}) # attempt to move from a non-existing key assert await redis_client.smove(non_existing_key, key1, "4") is False - assert await redis_client.smembers(key1) == convert_str_to_bytes_set({"2", "3"}) + assert await redis_client.smembers(key1) == convert_string_to_bytes_dict({"2", "3"}) # move to a new set assert await redis_client.smove(key1, key3, "2") @@ -1705,7 +1702,7 @@ async def test_sunion(self, redis_client: TGlideClient): # non-existing key returns the set of existing keys assert await redis_client.sunion( [key1, non_existing_key] - ) == convert_str_to_bytes_set(set(member1_list)) + ) == convert_string_to_bytes_dict(set(member1_list)) # non-set key assert await redis_client.set(key2, "value") == OK @@ -1728,19 +1725,19 @@ async def test_sunionstore(self, redis_client: TGlideClient): # store union in new key assert await redis_client.sunionstore(key4, [key1, key2]) == 5 - assert await redis_client.smembers(key4) == convert_str_to_bytes_set( + assert await redis_client.smembers(key4) == convert_string_to_bytes_dict( {"a", "b", "c", "d", "e"} ) # overwrite existing set assert await redis_client.sunionstore(key1, [key4, key2]) == 5 - assert await redis_client.smembers(key1) == convert_str_to_bytes_set( + assert await redis_client.smembers(key1) == convert_string_to_bytes_dict( {"a", "b", "c", "d", "e"} ) # overwrite one of the source keys assert await redis_client.sunionstore(key2, [key4, key2]) == 5 - assert await redis_client.smembers(key1) == convert_str_to_bytes_set( + assert await redis_client.smembers(key1) == convert_string_to_bytes_dict( {"a", "b", "c", "d", "e"} ) @@ -1755,7 +1752,7 @@ async def test_sunionstore(self, redis_client: TGlideClient): # overwrite destination when destination is not a set assert await redis_client.sunionstore(string_key, [key1, key3]) == 7 - assert await redis_client.smembers(string_key) == convert_str_to_bytes_set( + assert await redis_client.smembers(string_key) == convert_string_to_bytes_dict( { "a", "b", @@ -1779,7 +1776,7 @@ async def test_sinter(self, redis_client: TGlideClient): # positive test case assert await redis_client.sadd(key1, member1_list) == 3 assert await redis_client.sadd(key2, member2_list) == 3 - assert await redis_client.sinter([key1, key2]) == set([b"c"]) + assert await redis_client.sinter([key1, key2]) == {b"c"} # invalid argument - key list must not be empty with pytest.raises(RequestError): @@ -1895,16 +1892,16 @@ async def test_sdiff(self, redis_client: TGlideClient): assert await redis_client.sadd(key1, ["a", "b", "c"]) == 3 assert await redis_client.sadd(key2, ["c", "d", "e"]) == 3 - assert await redis_client.sdiff([key1, key2]) == convert_str_to_bytes_set( + assert await redis_client.sdiff([key1, key2]) == convert_string_to_bytes_dict( {"a", "b"} ) - assert await redis_client.sdiff([key2, key1]) == convert_str_to_bytes_set( + assert await redis_client.sdiff([key2, key1]) == convert_string_to_bytes_dict( {"d", "e"} ) assert await redis_client.sdiff( [key1, non_existing_key] - ) == convert_str_to_bytes_set({"a", "b", "c"}) + ) == convert_string_to_bytes_dict({"a", "b", "c"}) assert await redis_client.sdiff([non_existing_key, key1]) == set() # invalid argument - key list must not be empty @@ -1930,11 +1927,11 @@ async def test_sdiffstore(self, redis_client: TGlideClient): # Store diff in new key assert await redis_client.sdiffstore(key3, [key1, key2]) == 2 - assert await redis_client.smembers(key3) == convert_str_to_bytes_set({"a", "b"}) + assert await redis_client.smembers(key3) == convert_string_to_bytes_dict({"a", "b"}) # Overwrite existing set assert await redis_client.sdiffstore(key3, [key2, key1]) == 2 - assert await redis_client.smembers(key3) == convert_str_to_bytes_set({"d", "e"}) + assert await redis_client.smembers(key3) == convert_string_to_bytes_dict({"d", "e"}) # Overwrite one of the source sets assert await redis_client.sdiffstore(key3, [key2, key3]) == 1 @@ -1942,7 +1939,7 @@ async def test_sdiffstore(self, redis_client: TGlideClient): # Diff between non-empty set and empty set assert await redis_client.sdiffstore(key3, [key1, non_existing_key]) == 3 - assert await redis_client.smembers(key3) == convert_str_to_bytes_set( + assert await redis_client.smembers(key3) == convert_string_to_bytes_dict( {"a", "b", "c"} ) @@ -1961,7 +1958,7 @@ async def test_sdiffstore(self, redis_client: TGlideClient): # Overwrite a key holding a non-set value assert await redis_client.sdiffstore(string_key, [key1, key2]) == 2 - assert await redis_client.smembers(string_key) == convert_str_to_bytes_set( + assert await redis_client.smembers(string_key) == convert_string_to_bytes_dict( {"a", "b"} ) @@ -1994,7 +1991,7 @@ async def test_ltrim(self, redis_client: TGlideClient): assert await redis_client.lpush(key, value_list) == 4 assert await redis_client.ltrim(key, 0, 1) == OK - assert await redis_client.lrange(key, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key, 0, -1) == convert_string_to_bytes_dict( ["value1", "value2"] ) @@ -2017,12 +2014,12 @@ async def test_lrem(self, redis_client: TGlideClient): assert await redis_client.lpush(key, value_list) == 5 assert await redis_client.lrem(key, 2, "value1") == 2 - assert await redis_client.lrange(key, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key, 0, -1) == convert_string_to_bytes_dict( ["value2", "value2", "value1"] ) assert await redis_client.lrem(key, -1, "value2") == 1 - assert await redis_client.lrange(key, 0, -1) == convert_str_to_bytes_list( + assert await redis_client.lrange(key, 0, -1) == convert_string_to_bytes_dict( ["value2", "value1"] ) @@ -2370,7 +2367,7 @@ async def test_geosearch_by_box(self, redis_client: TGlideClient): GeospatialData(15, 37), GeoSearchByBox(400, 400, GeoUnit.KILOMETERS), OrderBy.ASC, - ) == convert_str_to_bytes_list(members) + ) == convert_string_to_bytes_dict(members) assert await redis_client.geosearch( key, @@ -2380,7 +2377,7 @@ async def test_geosearch_by_box(self, redis_client: TGlideClient): with_coord=True, with_dist=True, with_hash=True, - ) == convert_str_to_bytes_list(result[::-1]) + ) == convert_string_to_bytes_dict(result[::-1]) assert await redis_client.geosearch( key, @@ -2400,7 +2397,7 @@ async def test_geosearch_by_box(self, redis_client: TGlideClient): GeoSearchByBox(meters, meters, GeoUnit.METERS), OrderBy.DESC, with_dist=True, - ) == convert_str_to_bytes_list( + ) == convert_string_to_bytes_dict( [["edge2", [236529.1799]], ["Palermo", [166274.1516]], ["Catania", [0.0]]] ) @@ -2424,7 +2421,7 @@ async def test_geosearch_by_box(self, redis_client: TGlideClient): OrderBy.ASC, count=GeoSearchCount(1, True), ) - )[0] in convert_str_to_bytes_list(members) + )[0] in convert_string_to_bytes_dict(members) @pytest.mark.parametrize("cluster_mode", [True, False]) @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) @@ -2456,7 +2453,7 @@ async def test_geosearch_by_radius(self, redis_client: TGlideClient): "Catania", GeoSearchByRadius(feet, GeoUnit.FEET), OrderBy.ASC, - ) == convert_str_to_bytes_list(members[:2]) + ) == convert_string_to_bytes_dict(members[:2]) # Test search by radius, units: meters, from a member meters = 200 * 1000 @@ -2465,7 +2462,7 @@ async def test_geosearch_by_radius(self, redis_client: TGlideClient): "Catania", GeoSearchByRadius(meters, GeoUnit.METERS), OrderBy.DESC, - ) == convert_str_to_bytes_list(members[:2][::-1]) + ) == convert_string_to_bytes_dict(members[:2][::-1]) # Test search by radius, unit: miles, from a geospatial data assert await redis_client.geosearch( @@ -2473,7 +2470,7 @@ async def test_geosearch_by_radius(self, redis_client: TGlideClient): GeospatialData(15, 37), GeoSearchByRadius(175, GeoUnit.MILES), OrderBy.DESC, - ) == convert_str_to_bytes_list(members[::-1]) + ) == convert_string_to_bytes_dict(members[::-1]) # Test search by radius, unit: kilometers, from a geospatial data, with limited count to 2 assert await redis_client.geosearch( @@ -2485,7 +2482,7 @@ async def test_geosearch_by_radius(self, redis_client: TGlideClient): with_coord=True, with_dist=True, with_hash=True, - ) == convert_str_to_bytes_list(result) + ) == convert_string_to_bytes_dict(result) # Test search by radius, unit: kilometers, from a geospatial data, with limited ANY count to 1 assert ( @@ -2499,7 +2496,7 @@ async def test_geosearch_by_radius(self, redis_client: TGlideClient): with_dist=True, with_hash=True, ) - )[0] in convert_str_to_bytes_list(result) + )[0] in convert_string_to_bytes_dict(result) @pytest.mark.parametrize("cluster_mode", [True, False]) @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) @@ -2876,7 +2873,7 @@ async def test_geohash(self, redis_client: TGlideClient): assert await redis_client.geoadd(key, members_coordinates) == 2 assert await redis_client.geohash( key, ["Palermo", "Catania", "Place"] - ) == convert_str_to_bytes_list( + ) == convert_string_to_bytes_dict( [ "sqc8b49rny0", "sqdtr74hyu0", @@ -3655,10 +3652,10 @@ async def test_bzpopmin(self, redis_client: TGlideClient): assert await redis_client.zadd(key2, {"c": 2.0}) == 1 assert await redis_client.bzpopmin( [key1, key2], 0.5 - ) == convert_str_to_bytes_list([key1, "a", 1.0]) + ) == convert_string_to_bytes_dict([key1, "a", 1.0]) assert await redis_client.bzpopmin( [non_existing_key, key2], 0.5 - ) == convert_str_to_bytes_list( + ) == convert_string_to_bytes_dict( [ key2, "c", @@ -3714,10 +3711,10 @@ async def test_bzpopmax(self, redis_client: TGlideClient): assert await redis_client.zadd(key2, {"c": 2.0}) == 1 assert await redis_client.bzpopmax( [key1, key2], 0.5 - ) == convert_str_to_bytes_list([key1, "b", 1.5]) + ) == convert_string_to_bytes_dict([key1, "b", 1.5]) assert await redis_client.bzpopmax( [non_existing_key, key2], 0.5 - ) == convert_str_to_bytes_list( + ) == convert_string_to_bytes_dict( [ key2, "c", @@ -4284,7 +4281,7 @@ async def test_zdiff(self, redis_client: TGlideClient): compare_maps(await redis_client.zdiff_withscores([key1, key3]), {}) is True ) non_exist_res = await redis_client.zdiff_withscores([non_existing_key, key3]) - assert compare_maps(non_exist_res, {}) + assert non_exist_res == {} # invalid argument - key list must not be empty with pytest.raises(RequestError): @@ -4657,7 +4654,7 @@ async def test_sort_and_sort_store_with_get_or_by_args( get_patterns=["user:*->name"], alpha=True, ) - assert result == convert_str_to_bytes_list( + assert result == convert_string_to_bytes_dict( ["Dave", "Bob", "Alice", "Charlie", "Eve"] ) @@ -4669,7 +4666,7 @@ async def test_sort_and_sort_store_with_get_or_by_args( get_patterns=["user:*->name"], alpha=True, ) - assert result == convert_str_to_bytes_list( + assert result == convert_string_to_bytes_dict( [None, "Dave", "Bob", "Alice", "Charlie", "Eve"] ) @@ -4680,7 +4677,7 @@ async def test_sort_and_sort_store_with_get_or_by_args( get_patterns=["user:*->age"], alpha=True, ) - assert result == convert_str_to_bytes_list([None, "30", "25", "35", "20", "40"]) + assert result == convert_string_to_bytes_dict([None, "30", "25", "35", "20", "40"]) # Test Limit with count 0 result = await redis_client.sort( @@ -4839,7 +4836,7 @@ async def test_xadd_xtrim_xlen(self, redis_client: TGlideClient): assert await redis_client.xadd( key, [(field, "foo1"), (field2, "bar1")], StreamAddOptions(id="0-1") - ) == "0-1".encode("utf-8") + ) == b"0-1" assert ( await redis_client.xadd(key, [(field, "foo2"), (field2, "bar2")]) @@ -7861,36 +7858,36 @@ async def test_sscan(self, redis_client: GlideClusterClient): # Empty set result = await redis_client.sscan(key1, initial_cursor) - assert result[result_cursor_index] == initial_cursor + assert result[result_cursor_index] == initial_cursor.encode() assert result[result_collection_index] == [] # Negative cursor result = await redis_client.sscan(key1, "-1") - assert result[result_cursor_index] == initial_cursor + assert result[result_cursor_index] == initial_cursor.encode() assert result[result_collection_index] == [] # Result contains the whole set assert await redis_client.sadd(key1, char_members) == len(char_members) result = await redis_client.sscan(key1, initial_cursor) - assert result[result_cursor_index] == initial_cursor + assert result[result_cursor_index] == initial_cursor.encode() assert len(result[result_collection_index]) == len(char_members) - assert set(result[result_collection_index]).issubset(set(char_members)) + assert set(result[result_collection_index]).issubset(convert_string_to_bytes_dict(char_members)) result = await redis_client.sscan(key1, initial_cursor, match="a") - assert result[result_cursor_index] == initial_cursor - assert set(result[result_collection_index]).issubset(set(["a"])) + assert result[result_cursor_index] == initial_cursor.encode() + assert set(result[result_collection_index]).issubset(set([b"a"])) # Result contains a subset of the key assert await redis_client.sadd(key1, num_members) == len(num_members) result_cursor = "0" result_values = set() # type: set[str] - result = await redis_client.sscan(key1, result_cursor) + result = convert_bytes_to_string_dict(await redis_client.sscan(key1, result_cursor)) result_cursor = str(result[result_cursor_index]) result_values.update(result[result_collection_index]) # 0 is returned for the cursor of the last iteration. while result_cursor != "0": - next_result = await redis_client.sscan(key1, result_cursor) + next_result = convert_bytes_to_string_dict(await redis_client.sscan(key1, result_cursor)) next_result_cursor = str(next_result[result_cursor_index]) assert next_result_cursor != result_cursor diff --git a/python/python/tests/utils/utils.py b/python/python/tests/utils/utils.py index 1d33fce74a..864d75e0a8 100644 --- a/python/python/tests/utils/utils.py +++ b/python/python/tests/utils/utils.py @@ -4,7 +4,6 @@ from typing import Any, Dict, List, Mapping, Optional, TypeVar, Union, cast from glide.async_commands.core import InfoSection -from glide.async_commands.utils.utils import convert_bytes_to_string_dict from glide.constants import TResult from glide.glide_client import TGlideClient from packaging import version @@ -131,21 +130,60 @@ def compare_maps( convert_bytes_to_string_dict(map2) ) - -def convert_str_to_bytes_list(lst): - return [ - ( - elem.encode("utf-8") - if isinstance(elem, str) - else convert_str_to_bytes_list(elem) if isinstance(elem, list) else elem - ) - for elem in lst +def convert_bytes_to_string_dict( + # TODO: remove the str options + byte_string_dict: Optional[ + Union[Mapping[bytes, Any], Dict[bytes, Any], list[bytes, Any], Mapping[str, Any], Dict[str, Any]] ] - - -def convert_str_to_bytes_set(set): - return {elem.encode("utf-8") if isinstance(elem, str) else elem for elem in set} - +) -> Optional[Dict[str, Any]]: + """ + Recursively convert data structure from byte strings to regular strings, + handling nested data structures of any depth. + """ + if byte_string_dict is None: + return None + + def convert(item: Any) -> Any: + if isinstance(item, dict): + return {convert(key): convert(value) for key, value in item.items()} + elif isinstance(item, list): + return [convert(elem) for elem in item] + elif isinstance(item, set): + return {convert(elem) for elem in item} + elif isinstance(item, bytes): + return item.decode("utf-8") + else: + return item + + return convert(byte_string_dict) + + +def convert_string_to_bytes_dict( + # TODO: remove the bytes options + string_structure: Optional[ + Union[Mapping[bytes, Any], Dict[bytes, Any], list[str, Any], Mapping[str, Any], Dict[str, Any]] + ] +) -> Optional[Dict[bytes, Any]]: + """ + Recursively convert the data structure from strings to bytes, + handling nested data structures of any depth. + """ + if string_structure is None: + return None + + def convert(item: Any) -> Any: + if isinstance(item, dict): + return {convert(key): convert(value) for key, value in item.items()} + elif isinstance(item, list): + return [convert(elem) for elem in item] + elif isinstance(item, set): + return {convert(elem) for elem in item} + elif isinstance(item, str): + return item.encode("utf-8") + else: + return item + + return convert(string_structure) def generate_lua_lib_code( lib_name: str, functions: Mapping[str, str], readonly: bool