Skip to content

Commit b0bc3a0

Browse files
add commands
1 parent da933df commit b0bc3a0

File tree

7 files changed

+294
-20
lines changed

7 files changed

+294
-20
lines changed

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,33 @@ asyncio.run(test())
3636
3737
- `GET`: get value from key
3838
Ex: `await dice.get("key")`
39+
40+
- `DEL`: delete key
41+
Ex: `await dice.delete("key")`
42+
43+
- `EXISTS`: check if a key exists & returns bool
44+
Ex: `await dice.exists("key")`
45+
46+
- `EXPIRE`: sets ttl in seconds for a key
47+
Ex: `await dice.expire("key", 10)`
48+
49+
- `KEYS`: get all keys in a list according to the pattern give. Use "_" for all keys.
50+
Ex: `await dice.keys("_")`
51+
52+
- `FLUSH`: clear all keys
53+
Ex: `await dice.flush()`
54+
55+
- `INCR`: incr key by 1. Default value to start is 0 if key is not already set.
56+
Ex: `await dice.incr("key")`
57+
58+
- `INCRBY`: incr key by amount (int)
59+
Ex: `await dice.incrby("key", 2)`
60+
61+
- `DECR`: decr key by 1.
62+
Ex: `await dice.decr("key")`
63+
64+
- `DECRBY`: decr key by amount (int)
65+
Ex: `await dice.decrby("key", 2)`
66+
67+
- `TTL`: get TTL (time to live) of the key in seconds
68+
Ex: `await dice.ttl("key")`

dice_py/command/command.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,52 @@ async def get(self, key) -> str:
1313
async def set(self, key, value) -> str:
1414
"""Set the value of a key."""
1515
return await self._execute_command(f"SET {key} {value}")
16+
17+
async def delete(self, key) -> bool:
18+
"""Delete a key."""
19+
return await self._execute_command(f"DEL {key}") == "1"
20+
21+
async def exists(self, key) -> bool:
22+
"""Check if a key exists."""
23+
return await self._execute_command(f"EXISTS {key}") == "1"
24+
25+
async def expire(self, key: str, seconds: int) -> bool:
26+
"""Set a timeout on a key."""
27+
return await self._execute_command(f"EXPIRE {key} {seconds}") == "1"
28+
29+
async def keys(self, pattern: str) -> list:
30+
"""
31+
Get all keys matching a pattern.
32+
The pattern is a glob-style pattern. Use * as a wildcard for any number of characters.
33+
"""
34+
keys = await self._execute_command(f"KEYS {pattern}")
35+
return keys.split(" ") if keys else []
36+
37+
async def flush(self) -> bool:
38+
"""Delete all keys."""
39+
return await self._execute_command("FLUSHDB") == "OK"
40+
41+
async def incr(self, key) -> int:
42+
"""Increment the value of a key."""
43+
return int(await self._execute_command(f"INCR {key}"))
44+
45+
async def decr(self, key) -> int:
46+
"""Decrement the value of a key."""
47+
return int(await self._execute_command(f"DECR {key}"))
48+
49+
async def incrby(self, key: str, amount: int) -> int:
50+
"""Increment the value of a key by a specified amount."""
51+
return int(await self._execute_command(f"INCRBY {key} {amount}"))
52+
53+
async def decrby(self, key: str, amount: int) -> int:
54+
"""Decrement the value of a key by a specified amount."""
55+
return int(await self._execute_command(f"DECRBY {key} {amount}"))
56+
57+
async def ttl(self, key: str) -> int:
58+
"""
59+
Get the time to live for a key.
60+
positive integer: the remaining time to live in seconds
61+
-2: the key does not exist
62+
-1: the key exists but has no associated expire
63+
"""
64+
return int(await self._execute_command(f"TTL {key}"))

dice_py/command/executor.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ def _send_command(self, command: str, conn: Connect):
1616
"""Send a command to the socket."""
1717
conn.sock.sendall(command.encode("utf-8"))
1818
response = conn.sock.recv(4096).decode("utf-8")
19+
if response.startswith("*"):
20+
lines = response.split("\r\n")
21+
return " ".join(lines[2::2])
1922
return response.split("\r\n")[0][1:]
2023

2124
async def _execute_command(self, command: str):

docs/index.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,33 @@ asyncio.run(test())
3636
3737
- `GET`: get value from key
3838
Ex: `await dice.get("key")`
39+
40+
- `DEL`: delete key
41+
Ex: `await dice.delete("key")`
42+
43+
- `EXISTS`: check if a key exists & returns bool
44+
Ex: `await dice.exists("key")`
45+
46+
- `EXPIRE`: sets ttl in seconds for a key
47+
Ex: `await dice.expire("key", 10)`
48+
49+
- `KEYS`: get all keys in a list according to the pattern give. Use "_" for all keys.
50+
Ex: `await dice.keys("_")`
51+
52+
- `FLUSH`: clear all keys
53+
Ex: `await dice.flush()`
54+
55+
- `INCR`: incr key by 1. Default value to start is 0 if key is not already set.
56+
Ex: `await dice.incr("key")`
57+
58+
- `INCRBY`: incr key by amount (int)
59+
Ex: `await dice.incrby("key", 2)`
60+
61+
- `DECR`: decr key by 1.
62+
Ex: `await dice.decr("key")`
63+
64+
- `DECRBY`: decr key by amount (int)
65+
Ex: `await dice.decrby("key", 2)`
66+
67+
- `TTL`: get TTL (time to live) of the key in seconds
68+
Ex: `await dice.ttl("key")`

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description = "Python Client for DiceDB"
55
authors = ["Priyanshu Panwar <[email protected]>"]
66
readme = "README.md"
77
license = "MIT"
8-
homepage = "https://github.com/priyanshu-panwar/dice-py"
8+
homepage = "https://priyanshu-panwar.github.io/dice-py"
99
repository = "https://github.com/priyanshu-panwar/dice-py"
1010
keywords = ["dice", "dice-py", "dice-db", "dicedb", "python", "client"]
1111
classifiers = [

tests/command/test_command.py

Lines changed: 100 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,108 @@
11
import unittest
2-
from unittest.mock import patch
2+
from unittest.mock import patch, MagicMock
33
from dice_py.command.command import Command
4+
from dice_py.connection.client import DiceClient
45
import asyncio
56

67

78
class TestCommand(unittest.TestCase):
89

10+
def setUp(self):
11+
self.client = MagicMock(spec=DiceClient)
12+
self.command = Command(self.client)
13+
self.loop = asyncio.get_event_loop()
14+
915
@patch("dice_py.command.executor.Executor._execute_command", return_value="OK")
10-
@patch("dice_py.command.executor.DiceClient")
11-
def test_set(self, mock_client, mock_execute_command):
12-
command = Command(mock_client)
13-
loop = asyncio.get_event_loop()
14-
response = loop.run_until_complete(command.set("key", "value"))
15-
16-
mock_execute_command.assert_called_once_with("SET key value")
17-
self.assertEqual(response, "OK")
18-
19-
@patch("dice_py.command.executor.Executor._execute_command", return_value="value")
20-
@patch("dice_py.command.executor.DiceClient")
21-
def test_get(self, mock_client, mock_execute_command):
22-
command = Command(mock_client)
23-
loop = asyncio.get_event_loop()
24-
response = loop.run_until_complete(command.get("key"))
25-
26-
mock_execute_command.assert_called_once_with("GET key")
27-
self.assertEqual(response, "value")
16+
def test_set(self, mock_execute_command):
17+
set_response = self.loop.run_until_complete(
18+
self.command.set("test_key", "test_value")
19+
)
20+
mock_execute_command.assert_called_once_with("SET test_key test_value")
21+
self.assertEqual(set_response, "OK")
22+
23+
@patch(
24+
"dice_py.command.executor.Executor._execute_command", return_value="test_value"
25+
)
26+
def test_get(self, mock_execute_command):
27+
get_response = self.loop.run_until_complete(self.command.get("test_key"))
28+
mock_execute_command.assert_called_once_with("GET test_key")
29+
self.assertEqual(get_response, "test_value")
30+
31+
@patch("dice_py.command.executor.Executor._execute_command", return_value="1")
32+
def test_delete(self, mock_execute_command):
33+
delete_response = self.loop.run_until_complete(self.command.delete("test_key"))
34+
mock_execute_command.assert_called_once_with("DEL test_key")
35+
self.assertTrue(delete_response)
36+
37+
@patch("dice_py.command.executor.Executor._execute_command", return_value="1")
38+
def test_exists(self, mock_execute_command):
39+
exists_response = self.loop.run_until_complete(self.command.exists("test_key"))
40+
mock_execute_command.assert_called_once_with("EXISTS test_key")
41+
self.assertTrue(exists_response)
42+
43+
@patch("dice_py.command.executor.Executor._execute_command", return_value="1")
44+
def test_expire(self, mock_execute_command):
45+
expire_response = self.loop.run_until_complete(
46+
self.command.expire("test_key", 60)
47+
)
48+
mock_execute_command.assert_called_once_with("EXPIRE test_key 60")
49+
self.assertTrue(expire_response)
50+
51+
@patch(
52+
"dice_py.command.executor.Executor._execute_command",
53+
return_value="test_key test_key_2",
54+
)
55+
def test_keys(self, mock_execute_command):
56+
keys_response = self.loop.run_until_complete(self.command.keys("*"))
57+
mock_execute_command.assert_called_once_with("KEYS *")
58+
self.assertEqual(keys_response, ["test_key", "test_key_2"])
59+
60+
@patch(
61+
"dice_py.command.executor.Executor._execute_command",
62+
return_value="OK",
63+
)
64+
def test_flush(self, mock_execute_command):
65+
flush_response = self.loop.run_until_complete(self.command.flush())
66+
mock_execute_command.assert_called_once_with("FLUSHDB")
67+
self.assertTrue(flush_response)
68+
69+
@patch("dice_py.command.executor.Executor._execute_command", return_value="11")
70+
def test_incr(self, mock_execute_command):
71+
incr_response = self.loop.run_until_complete(self.command.incr("test_key"))
72+
mock_execute_command.assert_called_once_with("INCR test_key")
73+
self.assertEqual(incr_response, 11)
74+
75+
@patch("dice_py.command.executor.Executor._execute_command", return_value="9")
76+
def test_decr(self, mock_execute_command):
77+
decr_response = self.loop.run_until_complete(self.command.decr("test_key"))
78+
mock_execute_command.assert_called_once_with("DECR test_key")
79+
self.assertEqual(decr_response, 9)
80+
81+
@patch("dice_py.command.executor.Executor._execute_command", return_value="11")
82+
def test_incrby(self, mock_execute_command):
83+
_ = self.loop.run_until_complete(self.command.set("test_key", 6))
84+
incrby_response = self.loop.run_until_complete(
85+
self.command.incrby("test_key", 5)
86+
)
87+
self.assertEqual(incrby_response, 11)
88+
89+
@patch("dice_py.command.executor.Executor._execute_command", return_value="1")
90+
def test_decrby(self, mock_execute_command):
91+
_ = self.loop.run_until_complete(self.command.set("test_key", 6))
92+
decrby_response = self.loop.run_until_complete(
93+
self.command.decrby("test_key", 5)
94+
)
95+
self.assertEqual(decrby_response, 1)
96+
97+
@patch("dice_py.command.executor.Executor._execute_command", return_value="-1")
98+
def test_ttl_not_set(self, mock_execute_command):
99+
_ = self.loop.run_until_complete(self.command.set("test_key", 10))
100+
ttl_response = self.loop.run_until_complete(self.command.ttl("test_key"))
101+
self.assertEqual(ttl_response, -1)
102+
103+
@patch("dice_py.command.executor.Executor._execute_command", return_value="10")
104+
def test_ttl(self, mock_execute_command):
105+
_ = self.loop.run_until_complete(self.command.set("test_key", 10))
106+
_ = self.loop.run_until_complete(self.command.expire("test_key", 10))
107+
ttl_response = self.loop.run_until_complete(self.command.ttl("test_key"))
108+
self.assertEqual(ttl_response, 10)

tests/test_dice.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,87 @@ def test_get(self):
2121
get_response = self.loop.run_until_complete(self.dice.get("test_key"))
2222
self.assertEqual(get_response, "10")
2323

24+
def test_delete(self):
25+
# First, set the key to ensure it exists
26+
self.loop.run_until_complete(self.dice.set("test_key", 10))
27+
delete_response = self.loop.run_until_complete(self.dice.delete("test_key"))
28+
self.assertTrue(delete_response)
29+
30+
def test_exists(self):
31+
# First, set the key to ensure it exists
32+
self.loop.run_until_complete(self.dice.set("test_key", 10))
33+
exists_response = self.loop.run_until_complete(self.dice.exists("test_key"))
34+
self.assertTrue(exists_response)
35+
36+
def test_not_exists(self):
37+
exists_response = self.loop.run_until_complete(self.dice.exists("test_key"))
38+
self.assertFalse(exists_response)
39+
40+
async def test_expire(self):
41+
# First, set the key to ensure it exists
42+
self.loop.run_until_complete(self.dice.set("test_key", 10))
43+
expire_response = self.loop.run_until_complete(self.dice.expire("test_key", 10))
44+
self.assertTrue(expire_response)
45+
# Check if the key exists after 10 seconds
46+
asyncio.sleep(10)
47+
exists_response = self.loop.run_until_complete(self.dice.exists("test_key"))
48+
self.assertFalse(exists_response)
49+
50+
async def test_keys(self):
51+
# First, set the key to ensure it exists
52+
self.loop.run_until_complete(self.dice.set("test_key", 10))
53+
self.loop.run_until_complete(self.dice.set("test_key_2", 20))
54+
keys_response = self.loop.run_until_complete(self.dice.keys("*"))
55+
self.assertIn(["test_key", "test_key_2"], keys_response)
56+
57+
def test_flush(self):
58+
# First, set the key to ensure it exists
59+
self.loop.run_until_complete(self.dice.set("test_key", 10))
60+
self.loop.run_until_complete(self.dice.set("test_key_2", 20))
61+
flush_response = self.loop.run_until_complete(self.dice.flush())
62+
self.assertTrue(flush_response)
63+
64+
def test_incr(self):
65+
# First, set the key to ensure it exists
66+
self.loop.run_until_complete(self.dice.set("test_key", 10))
67+
incr_response = self.loop.run_until_complete(self.dice.incr("test_key"))
68+
self.assertEqual(incr_response, 11)
69+
70+
def test_decr(self):
71+
# First, set the key to ensure it exists
72+
self.loop.run_until_complete(self.dice.set("test_key", 10))
73+
decr_response = self.loop.run_until_complete(self.dice.decr("test_key"))
74+
self.assertEqual(decr_response, 9)
75+
76+
def test_incrby(self):
77+
# First, set the key to ensure it exists
78+
self.loop.run_until_complete(self.dice.set("test_key", 10))
79+
incrby_response = self.loop.run_until_complete(self.dice.incrby("test_key", 5))
80+
self.assertEqual(incrby_response, 15)
81+
82+
def test_decrby(self):
83+
# First, set the key to ensure it exists
84+
self.loop.run_until_complete(self.dice.set("test_key", 10))
85+
decrby_response = self.loop.run_until_complete(self.dice.decrby("test_key", 5))
86+
self.assertEqual(decrby_response, 5)
87+
88+
def test_ttl_not_set(self):
89+
# First, set the key to ensure it exists
90+
self.loop.run_until_complete(self.dice.set("test_key", 10))
91+
ttl_response = self.loop.run_until_complete(self.dice.ttl("test_key"))
92+
self.assertEqual(ttl_response, -1)
93+
94+
def test_ttl_set(self):
95+
# First, set the key to ensure it exists
96+
self.loop.run_until_complete(self.dice.set("test_key", 10))
97+
self.loop.run_until_complete(self.dice.expire("test_key", 10))
98+
ttl_response = self.loop.run_until_complete(self.dice.ttl("test_key"))
99+
self.assertEqual(ttl_response, 9)
100+
101+
def test_ttl_not_exists(self):
102+
ttl_response = self.loop.run_until_complete(self.dice.ttl("test_key"))
103+
self.assertEqual(ttl_response, -2)
104+
24105
def tearDown(self):
25106
# Clean up by deleting the test key
26107
self.loop.run_until_complete(self.dice._execute_command("DEL test_key"))

0 commit comments

Comments
 (0)