Skip to content

Commit

Permalink
Added hi redis parser
Browse files Browse the repository at this point in the history
  • Loading branch information
generalpy101 committed Jun 30, 2024
1 parent 509d5be commit 0b8a032
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 12 deletions.
Empty file added redis_clone/parser/__init__.py
Empty file.
7 changes: 7 additions & 0 deletions redis_clone/parser/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from abc import ABC, abstractmethod
from typing import Tuple, List, AnyStr

class BaseParser(ABC):
@abstractmethod
def parse(self, data, *args, **kwargs) -> Tuple[AnyStr, List[AnyStr]]:
pass
18 changes: 18 additions & 0 deletions redis_clone/parser/hi_redis_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from hiredis import Reader

from redis_clone.parser.base import BaseParser


class HiRedisParser(BaseParser):
def __init__(self):
self.reader_class = Reader

def parse(self, data, *args, **kwargs):
reader = self.reader_class(*args, **kwargs)
reader.feed(data)
if data := reader.gets():
command = data[0]
arguments = data[1:]
return command, arguments

raise Exception("Invalid data received")
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from enum import Enum

from redis_clone.parser.base import BaseParser

PROTOCOL_SEPARATOR = b'\r\n'

COMMANDS_METADATA = {
Expand Down Expand Up @@ -31,11 +33,11 @@ class Protocol_2_Data_Types(Enum):
ARRAY = b"*"


class Parser:
class Parser(BaseParser):
def __init__(self, protocol_version) -> None:
self.protocol_version = protocol_version

def parse_client_request(self, data):
def parse(self, data, *args, **kwargs):
"""
This function parses the client request and returns the command name and arguments
"""
Expand Down
2 changes: 1 addition & 1 deletion redis_clone/response_builder.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from redis_clone.redis_parser import Protocol_2_Data_Types, PROTOCOL_SEPARATOR
from redis_clone.parser.redis_parser import Protocol_2_Data_Types, PROTOCOL_SEPARATOR


class ResponseBuilder:
Expand Down
9 changes: 5 additions & 4 deletions redis_clone/server.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import time
import sys
import os
import asyncio
import logging


from enum import Enum
from redis_clone.redis_parser import Parser, Protocol_2_Data_Types
from hiredis import Reader
from redis_clone.parser.redis_parser import Parser, Protocol_2_Data_Types
from redis_clone.parser.hi_redis_parser import HiRedisParser
from redis_clone.response_builder import ResponseBuilder

logger = logging.getLogger(__name__)

HOST = os.environ.get("REDIS_HOST", "0.0.0.0")
PORT = os.environ.get("REDIS_PORT", 9999)
PORT = int(os.environ.get("REDIS_PORT", 9999))


class Protocol_2_Commands(Enum):
Expand Down Expand Up @@ -94,7 +95,7 @@ async def _handle_connection(self, reader, writer):
break

logger.info(f"Received data: {data}")
command_name, command_args = self.parser.parse_client_request(data)
command_name, command_args = self.parser.parse(data, encoding="utf-8", errors="strict")
logger.info(f"Command name: {command_name}")
logger.info(f"Command args: {command_args}")
response = self._process_command(command_name, command_args)
Expand Down
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
async-timeout==4.0.3
exceptiongroup==1.1.3
hiredis==2.3.2
iniconfig==2.0.0
packaging==23.2
pluggy==1.3.0
Expand Down
10 changes: 5 additions & 5 deletions tests/test_client_parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Using pytest for tests
from redis_clone.redis_parser import Parser, Protocol_2_Data_Types
from redis_clone.parser.redis_parser import Parser, Protocol_2_Data_Types


class TestParserClient:
Expand All @@ -11,7 +11,7 @@ def test_initial_command_request(self):

# Test initial connection
test_str = b"*1\r\n$7\r\nCOMMAND\r\n"
command, args = self.parser.parse_client_request(test_str)
command, args = self.parser.parse(test_str)

assert command == "COMMAND"
assert args == []
Expand All @@ -21,7 +21,7 @@ def test_set_command_request(self):
Test SET command request
"""
test_str = b"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"
command, args = self.parser.parse_client_request(test_str)
command, args = self.parser.parse(test_str)

assert command == "SET"
assert args == ["mykey", "myvalue"]
Expand All @@ -31,7 +31,7 @@ def test_get_command_request(self):
Test GET command request
"""
test_str = b"*2\r\n$3\r\nGET\r\n$5\r\nmykey\r\n"
command, args = self.parser.parse_client_request(test_str)
command, args = self.parser.parse(test_str)

assert command == "GET"
assert args == ["mykey"]
Expand All @@ -42,7 +42,7 @@ def test_subargs_parsing(self):
eg: SET mykey myvalue EX 10 NX
'''
test_str = b"*6\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n$2\r\nEX\r\n$2\r\n10\r\n$2\r\nNX\r\n"
command, args = self.parser.parse_client_request(test_str)
command, args = self.parser.parse(test_str)

print(args)
assert command == "SET"
Expand Down

0 comments on commit 0b8a032

Please sign in to comment.