Skip to content

Commit

Permalink
Python: add SETBIT command
Browse files Browse the repository at this point in the history
  • Loading branch information
aaron-congo committed Jun 14, 2024
1 parent ce82f5a commit 55dfffc
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
* Node: Added ZINTERCARD command ([#1553](https://github.com/aws/glide-for-redis/pull/1553))
* Python: Added LMPOP and BLMPOP commands ([#1547](https://github.com/aws/glide-for-redis/pull/1547))
* Node: Added OBJECT IDLETIME command ([#1567](https://github.com/aws/glide-for-redis/pull/1567))
* Python: Added SETBIT command ([#1571](https://github.com/aws/glide-for-redis/pull/1571))

### Breaking Changes
* Node: Update XREAD to return a Map of Map ([#1494](https://github.com/aws/glide-for-redis/pull/1494))
Expand Down
53 changes: 26 additions & 27 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4033,6 +4033,32 @@ async def pfmerge(self, destination: str, source_keys: List[str]) -> TOK:
),
)

async def setbit(self, key: str, offset: int, value: int) -> int:
"""
Sets or clears the bit at `offset` in the string value stored at `key`. The `offset` is a zero-based index,
with `0` being the first element of the list, `1` being the next element, and so on. The `offset` must be less
than `2^32` and greater than or equal to `0`. If a key is non-existent then the bit at `offset` is set to
`value` and the preceding bits are set to `0`.
See https://valkey.io/commands/setbit for more details.
Args:
key (str): The key of the string.
offset (int): The index of the bit to be set.
value (int): The bit value to set at `offset`. The value must be `0` or `1`.
Returns:
int: The bit value that was previously stored at `offset`.
Examples:
>>> await client.setbit("string_key", 1, 1)
0 # The second bit value was 0 before setting to 1.
"""
return cast(
int,
await self._execute_command(RequestType.SetBit, [key, str(offset), str(value)]),
)

async def object_encoding(self, key: str) -> Optional[str]:
"""
Returns the internal encoding for the Redis object stored at `key`.
Expand Down Expand Up @@ -4119,30 +4145,3 @@ async def object_refcount(self, key: str) -> Optional[int]:
Optional[int],
await self._execute_command(RequestType.ObjectRefCount, [key]),
)

async def setbit(self, key: str) -> Optional[int]:
"""
Sets or clears the bit at `offset` in the string value stored at `key`.
The `offset` is a zero-based index, with `0` being the first element of
the list, `1` being the next element, and so on. The <code>offset</code> must be
less than <code>2^32</code> and greater than or equal to <code>0</code>. If a key is
non-existent then the bit at <code>offset</code> is set to <code>value</code> and the preceding
bits are set to <code>0</code>.
See https://valkey.io/commands/object-refcount for more details.
Args:
key (str): The key of the object to get the reference count of.
Returns:
Optional[int]: If `key` exists, returns the reference count of the object stored at `key` as an integer.
Otherwise, returns None.
Examples:
>>> await client.object_refcount("my_hash")
2 # "my_hash" has a reference count of 2.
"""
return cast(
Optional[int],
await self._execute_command(RequestType.ObjectRefCount, [key]),
)
19 changes: 19 additions & 0 deletions python/python/glide/async_commands/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -2775,6 +2775,25 @@ def pfmerge(
"""
return self.append_command(RequestType.PfMerge, [destination] + source_keys)

def setbit(self: TTransaction, key: str, offset: int, value: int) -> TTransaction:
"""
Sets or clears the bit at `offset` in the string value stored at `key`. The `offset` is a zero-based index,
with `0` being the first element of the list, `1` being the next element, and so on. The `offset` must be less
than `2^32` and greater than or equal to `0`. If a key is non-existent then the bit at `offset` is set to
`value` and the preceding bits are set to `0`.
See https://valkey.io/commands/setbit for more details.
Args:
key (str): The key of the string.
offset (int): The index of the bit to be set.
value (int): The bit value to set at `offset`. The value must be `0` or `1`.
Command response:
int: The bit value that was previously stored at `offset`.
"""
return self.append_command(RequestType.SetBit, [key, str(offset), str(value)])

def object_encoding(self: TTransaction, key: str) -> TTransaction:
"""
Returns the internal encoding for the Redis object stored at `key`.
Expand Down
18 changes: 18 additions & 0 deletions python/python/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4029,6 +4029,24 @@ async def test_pfmerge(self, redis_client: TRedisClient):
with pytest.raises(RequestError):
assert await redis_client.pfmerge(string_key, [key3])

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_setbit(self, redis_client: TRedisClient):
key = get_random_string(10)
string_key = get_random_string(10)

assert await redis_client.setbit(key, 0, 1) == 0
assert await redis_client.setbit(key, 0, 0) == 1

# invalid argument - offset can't be negative
with pytest.raises(RequestError):
assert await redis_client.setbit(key, -1, 0) == 1

# key exists, but it is not a string
assert await redis_client.set(string_key, "foo")
with pytest.raises(RequestError):
assert await redis_client.setbit(string_key, 0, 0)

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_object_encoding(self, redis_client: TRedisClient):
Expand Down
6 changes: 6 additions & 0 deletions python/python/tests/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ async def transaction_test(
key16 = "{{{}}}:{}".format(keyslot, get_random_string(3)) # sorted set
key17 = "{{{}}}:{}".format(keyslot, get_random_string(3)) # sort
key18 = "{{{}}}:{}".format(keyslot, get_random_string(3)) # sort
key19 = "{{{}}}:{}".format(keyslot, get_random_string(3)) # bitmap

value = datetime.now(timezone.utc).strftime("%m/%d/%Y, %H:%M:%S")
value2 = get_random_string(5)
Expand Down Expand Up @@ -344,6 +345,11 @@ async def transaction_test(
transaction.pfcount([key10])
args.append(3)

transaction.setbit(key19, 1, 1)
args.append(0)
transaction.setbit(key19, 1, 0)
args.append(1)

transaction.geoadd(
key12,
{
Expand Down

0 comments on commit 55dfffc

Please sign in to comment.