Skip to content

Commit

Permalink
Add step 3 get and set into Redis Server
Browse files Browse the repository at this point in the history
  • Loading branch information
ngokchaoho committed Dec 16, 2023
1 parent 058afec commit 485a4f0
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 26 deletions.
30 changes: 28 additions & 2 deletions pyredis/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,45 @@ def _handle_ping(command):
return Error(data="ERR wrong number of arguments for 'ping' command")


def _handle_unrecognised_command(command):
def _handle_set(command, datastore):
if len(command) >= 3:
key = command[1].data.decode()
value = command[2].data.decode()
datastore[key] = value
return SimpleString("OK")
return Error("ERR wrong number of arguments for 'set' command")


def _handle_get(command, datastore):
if len(command) == 2:
key = command[1].data.decode()
try:
value = datastore[key]
except KeyError:
return BulkString(None)
return BulkString(value)
return Error("ERR wrong numer of arguments for 'get' command")


def _handle_unrecognised_command(command, *args):
args = " ".join((f"'{c.data.decode()}'" for c in command[1:]))
return Error(
f"ERR unknown command '{command[0].data.decode()}', with args beginning with: {args}"
)


def handle_command(command):
def handle_command(command, datastore):
match command[0].data.decode().upper():
case "ECHO":
return _handle_echo(command)

case "PING":
return _handle_ping(command)

case "SET":
return _handle_set(command, datastore)

case "GET":
return _handle_get(command, datastore)

return _handle_unrecognised_command(command)
10 changes: 10 additions & 0 deletions pyredis/datastore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class DataStore:
def __init__(self):
self._data = dict()

def __getitem__(self, key):
item = self._data[key]
return item

def __setitem__(self, key, value):
self._data[key] = value
45 changes: 23 additions & 22 deletions pyredis/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,17 @@

from pyredis.protocol import extract_frame_from_buffer, encode_message
from pyredis.commands import handle_command
from pyredis.datastore import DataStore

RECV_SIZE = 2048
log = logging.getLogger("pyredis")


def handle_client_connection(client_socket):
buffer = bytearray()
try:
while True:
data = client_socket.recv(RECV_SIZE)
log.info("Received data from client")
if not data:
break
buffer.extend(data)
frame, frame_size = extract_frame_from_buffer(buffer)
log.info("Extracted one frame from received data")
if frame:
buffer = buffer[frame_size:]
log.info("Processing one frame")
result = handle_command(frame)
client_socket.send(encode_message(result))

finally:
client_socket.close()


class Server:
def __init__(self, port) -> None:
self.port = port
self._running = False
self._datastore = DataStore()

def run(self):
self._running = True
Expand All @@ -47,7 +28,27 @@ def run(self):
while self._running:
client_socket, _ = server_socket.accept()
log.info("Accepted one client")
handle_client_connection(client_socket)
self.handle_client_connection(client_socket, self._datastore)

def handle_client_connection(self, client_socket, datastore):
buffer = bytearray()
try:
while True:
data = client_socket.recv(RECV_SIZE)
log.info("Received data from client")
if not data:
break
buffer.extend(data)
frame, frame_size = extract_frame_from_buffer(buffer)
log.info("Extracted one frame from received data")
if frame:
buffer = buffer[frame_size:]
log.info("Processing one frame")
result = handle_command(frame, datastore)
client_socket.send(encode_message(result))

finally:
client_socket.close()

def stop(self):
self._running = False
29 changes: 27 additions & 2 deletions tests/test_commands.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import pytest

from pyredis.commands import handle_command
from pyredis.datastore import DataStore
from pyredis.types import Array, BulkString, Error, Integer, SimpleString


@pytest.fixture(scope="module")
def datastore():
datastore = DataStore()
datastore["always_exist_key"] = "default"
return datastore


@pytest.mark.parametrize(
"command, expected",
[
Expand All @@ -19,9 +27,26 @@
),
# Ping Tests
(Array([BulkString(b"ping")]), SimpleString("PONG")),
# Set Tests
(Array([BulkString(b"ping"), BulkString(b"Hello")]), BulkString("Hello")),
(
Array([BulkString(b"set")]),
Error("ERR wrong number of arguments for 'set' command"),
),
(
Array([BulkString(b"set"), SimpleString(b"key")]),
Error("ERR wrong number of arguments for 'set' command"),
),
(
Array([BulkString(b"set"), SimpleString(b"key"), SimpleString(b"value")]),
SimpleString("OK"),
),
(
Array([BulkString(b"get"), SimpleString(b"always_exist_key")]),
BulkString("default"),
),
],
)
def test_handle_command(command, expected):
result = handle_command(command)
def test_handle_command(command, expected, datastore):
result = handle_command(command, datastore)
assert result == expected
6 changes: 6 additions & 0 deletions tests/test_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,9 @@ def test_read_frame(buffer, expected):
def test_encode_message(message, expected):
encoded_message = encode_message(message)
assert encoded_message == expected


def test_set_and_get_item():
ds = DataStore()
ds["key"] = 1
assert ds["key"] == 1

0 comments on commit 485a4f0

Please sign in to comment.