Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "srpcLib"
version = "2.0.0"
version = "2.1.0"
description = "SRPC is a lightweight Remote Procedure Call (RPC) library."
authors = [{ name="Oseas Andre", email="oseasandrepro@gmail.com" }]
dependencies = ["pytest>=8.4.1"]
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ flake8>=3.4.0
isort>=5.13.2
rich>=13.7.0
msgpack==1.2.1
mypy>=2.3.0
8 changes: 4 additions & 4 deletions srpcLib/stub_generator/srpc_server_stub_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def stop(self):
f.write(code)

logger.info(f"Server stub successfully generated: {server_stub_file_name}")
logger.info(
f"You must implement the Class '{server_class_name}' that implements '{interface_name}', "
f"inside '{module_name}.py' file."
)
# logger.info(
# f"You must implement the Class '{server_class_name}' that implements '{interface_name}', "
# f"inside '{module_name}.py' file."
# )
3 changes: 3 additions & 0 deletions srpcLib/tools/srpc_stub_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from ..stub_generator.srpc_client_stub_gen import gen_client_stub
from ..stub_generator.srpc_server_stub_gen import gen_server_stub
from ..utils.srpc_stub_util import (
check_service_defination,
get_interface_from_module,
get_methodname_signature_map_from_interface,
load_module_from_path,
Expand Down Expand Up @@ -34,6 +35,8 @@ def configure_logging():
file_name = path.basename(args.path)
interface_name = interface.__name__

check_service_defination(args.path)

gen_client_stub(
file_name,
interface_name,
Expand Down
40 changes: 40 additions & 0 deletions srpcLib/utils/srpc_stub_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import inspect
import logging
import os
import subprocess
import sys
from abc import ABC
from types import ModuleType
Expand Down Expand Up @@ -99,3 +100,42 @@ def build_param_tuple(params: list[str]) -> str:
return f"({params[0]},)"
else:
return f"({', '.join(params)})"


def get_service_name(full_interface_path: str):
full_interface_path = os.path.basename(full_interface_path).split(".")[0]
return full_interface_path.split("_")[0]


def get_service_dir(full_interface_path: str):
return os.path.dirname(full_interface_path)


def check_file_type_hints(file_path: str) -> tuple[bool, str]:
result = subprocess.run(
["mypy", "--disallow-untyped-defs", file_path], capture_output=True, text=True
)
output = result.stdout
if output[0:7] == "Success":
return True, ""
else:
return False, output


def check_service_defination(full_interface_path: str):
service_name = get_service_name(full_interface_path)
service_dir = get_service_dir(full_interface_path)

interface_impl_full_path = f"{service_dir}/{service_name}.py"

try:
passed, msg = check_file_type_hints(full_interface_path)
if not passed:
raise ValueError(f"Check type hint.\n{msg}")

passed, msg = check_file_type_hints(interface_impl_full_path)
if not passed:
raise ValueError(f"Check type hint.\n{msg}")
except ValueError as e:
logger.error(str(e))
exit(1)
2 changes: 1 addition & 1 deletion tests/integration/simple_project/calc/calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ def sub(self, a: int, b: int) -> int:
def mult(self, a: int, b: int) -> int:
return a * b

def div(self, a: int, b: int) -> int:
def div(self, a: int, b: int) -> float:
return a / b
2 changes: 1 addition & 1 deletion tests/integration/simple_project/calc/calc_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ def mult(self, a: int, b: int) -> int:
pass

@abstractmethod
def div(self, a: int, b: int) -> int:
def div(self, a: int, b: int) -> float:
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from abc import ABC, abstractmethod


class CalcinconsistentInterface(ABC):
@abstractmethod
def add(self, a, b) -> int:
pass

@abstractmethod
def sub(self, a: int, b: int) -> int:
pass

@abstractmethod
def mult(self, a: int, b: int) -> int:
pass

@abstractmethod
def div(self, a: int, b: int) -> float:
pass
22 changes: 22 additions & 0 deletions tests/integration/test_simple_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
SERVER_STUB_SCRIPT = "srpc_calc_server_stub.py"
CLIENT_STUB_SCRIPT = "srpc_calc_client_stub.py"
INTERFACE_DEF = TEST_PROJECT / "calc/calc_interface.py"
INTERFACE_DEF_INCONSISTENT = TEST_PROJECT / "calc/calcinconsistent_interface.py"

SERVER_SCRIPT = "server.py"
CLIENT_SCRIPT = "client.py"
Expand Down Expand Up @@ -111,6 +112,27 @@ def finalizer():
yield proc


def test_type_hint_checker_fail():
result = subprocess.run(
[sys.executable, "-m", f"{STUB_GEN_SCRIPT}", f"{INTERFACE_DEF_INCONSISTENT}"],
cwd=TEST_PROJECT,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)

expected_output = "INFO | srpcLib.utils.srpc_stub_util : Interface CalcinconsistentInterface found in module calcinconsistent_interface.\nERROR | srpcLib.utils.srpc_stub_util : Check type hint.\ncalc/calcinconsistent_interface.py:6: error: Function is missing a type annotation for one or more parameters [no-untyped-def]\nFound 1 error in 1 file (checked 1 source file)\n\n"
assert result.stderr[-351:] == expected_output, "Expect hint checker fail"


def test_type_hint_checker_sucess(generate_stubs):
# Assert generated scripts exist
server_file = TEST_PROJECT / SERVER_STUB_SCRIPT
client_file = TEST_PROJECT / CLIENT_STUB_SCRIPT
assert server_file.exists(), f"Missing server stub: {server_file}"
assert client_file.exists(), f"Missing client stub: {client_file}"


def test_rpc_the_for_operations_of_calc(server_process, generate_stubs):
result = subprocess.run(
[sys.executable, CLIENT_SCRIPT],
Expand Down
2 changes: 1 addition & 1 deletion tests/test_resources/calc/calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ def sub(self, a: int, b: int) -> int:
def mult(self, a: int, b: int) -> int:
return a * b

def div(self, a: int, b: int) -> int:
def div(self, a: int, b: int) -> float:
return a / b
2 changes: 1 addition & 1 deletion tests/test_resources/calc/calc_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ def mult(self, a: int, b: int) -> int:
pass

@abstractmethod
def div(self, a: int, b: int) -> int:
def div(self, a: int, b: int) -> float:
pass
19 changes: 19 additions & 0 deletions tests/test_resources/calc/calcinconsistent_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from abc import ABC, abstractmethod


class CalcinconsistentInterface(ABC):
@abstractmethod
def add(self, a, b) -> int:
pass

@abstractmethod
def sub(self, a: int, b: int) -> int:
pass

@abstractmethod
def mult(self, a: int, b: int) -> int:
pass

@abstractmethod
def div(self, a: int, b: int) -> float:
pass
10 changes: 10 additions & 0 deletions tests/unit/test_srpc_stub_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,13 @@ def testload_module_from_path(self):
path = "tests/test_resources/calc/calc_interface.py"
module = srpc_stub_util.load_module_from_path(path)
assert module.__name__ == "calc_interface"

def test_check_file_type_hints_sucess(self):
file_path = "tests/test_resources/calc/calc_interface.py"
passed, msg = srpc_stub_util.check_file_type_hints(file_path)
assert passed is True

def test_check_file_type_hints_fail(self):
file_path = "tests/test_resources/calc/calcinconsistent_interface.py."
passed, msg = srpc_stub_util.check_file_type_hints(file_path)
assert passed is False