From e12228abdaa582753c10e520611ae75454598621 Mon Sep 17 00:00:00 2001 From: Apostol Fet Date: Sat, 6 Dec 2025 15:43:31 +0300 Subject: [PATCH 1/2] feat(jsonrpc): use func name for jsonrpc method by default --- src/descanso/jsonrpc.py | 35 +++++++++++++++++++---- tests/builders/test_jsonrpc.py | 52 ++++++++++++++++++++++++++++++++-- tests/http/test_aiohttp.py | 6 ++-- tests/http/test_httpx.py | 6 ++-- tests/http/test_requests.py | 6 ++-- 5 files changed, 88 insertions(+), 17 deletions(-) diff --git a/src/descanso/jsonrpc.py b/src/descanso/jsonrpc.py index 2dcbd0b..49d78b6 100644 --- a/src/descanso/jsonrpc.py +++ b/src/descanso/jsonrpc.py @@ -291,6 +291,7 @@ def _add_body_transformer(self, spec: MethodSpec): self._add_request_transformer(spec, Body(field.name)) def _add_default_request_body_transformers(self, spec: MethodSpec): + self._add_default_jsonrpc_method(spec) self._add_body_transformer(spec) if self._get_body_field(spec): @@ -357,6 +358,18 @@ def _add_default_response_transformers(self, spec: MethodSpec) -> None: BodyModelLoad(spec.result_type, loader=loader), ) + def _add_default_jsonrpc_method(self, spec: MethodSpec) -> None: + jsonrpc_method_transformer = next( + ( + field + for field in spec.request_transformers + if isinstance(field, JsonRPCMethod) + ), + None, + ) + if jsonrpc_method_transformer is None: + self._add_request_transformer(spec, JsonRPCMethod(spec.name)) + @overload def decorate( self, @@ -403,22 +416,34 @@ def __call__( @overload def __call__( self, - method: str, + transformer: Transformer | None = None, *transformers: Transformer, + method: str | None = None, **params: Unpack[BuilderParams], ) -> "JsonRPCBuilder": ... def __call__( self, - func_or_method: str | Callable, + func_or_transformer: Callable | Transformer | None = None, *transformers: Transformer, + method: str | None = None, **params: Unpack[BuilderParams], ) -> Any: if transformers or params: instance = self.with_params(*transformers, **params) else: instance = self - if isinstance(func_or_method, str): - return instance.with_params(JsonRPCMethod(func_or_method)) + + additional_transformers = ( + (JsonRPCMethod(method),) if method is not None else () + ) + + if func_or_transformer is None: + return instance.with_params(*additional_transformers) + elif isinstance(func_or_transformer, Transformer): + return instance.with_params( + func_or_transformer, + *additional_transformers, + ) else: - return instance.decorate(func_or_method) + return instance.decorate(func_or_transformer) diff --git a/tests/builders/test_jsonrpc.py b/tests/builders/test_jsonrpc.py index a419110..c3bf00f 100644 --- a/tests/builders/test_jsonrpc.py +++ b/tests/builders/test_jsonrpc.py @@ -1,3 +1,5 @@ +from dirty_equals import IsList + from descanso import JsonRPCBuilder from descanso.jsonrpc import ( JsonRPCErrorRaiser, @@ -27,7 +29,7 @@ def test_url(): jsonrpc = JsonRPCBuilder(url="/foo") class Api: - @jsonrpc("methodname") + @jsonrpc(method="methodname") def do(self, body: int) -> Model: """Hello""" @@ -78,7 +80,7 @@ def test_params(): ) class Api: - @jsonrpc("methodname") + @jsonrpc(method="methodname") def do(self, data: int) -> Model: """Hello""" @@ -109,7 +111,7 @@ def test_override(): jsonrpc = JsonRPCBuilder(url="/foo") class Api: - @jsonrpc("methodname", url="/bar") + @jsonrpc(method="methodname", url="/bar") def do(self, data: int) -> Model: """Hello""" @@ -130,3 +132,47 @@ def do(self, data: int) -> Model: dirty[JsonRPCErrorRaiser](), dirty[UnpackJsonRPC](), ] + + +def test_default_jsonrpc_method() -> None: + jsonrpc = JsonRPCBuilder(url="/foo") + + class Api: + @jsonrpc + def do(self, data: int) -> Model:... + + @jsonrpc() + def work(self, data: int) -> Model:... + + + assert Api.do.spec.request_transformers == IsList( + dirty[JsonRPCMethod](method="do"), + length=..., + ) + assert Api.work.spec.request_transformers == IsList( + dirty[JsonRPCMethod](method="work"), + length=..., + ) + + +def test_default_jsonrpc_method_with_transformers_and_params() -> None: + jsonrpc = JsonRPCBuilder(url="/foo") + req_transformer = Skip("0") + res_transformer = ErrorRaiser(codes=[200]) + + class Api: + @jsonrpc(req_transformer, res_transformer, url="/bar") + def do(self, data: int) -> Model:... + + assert Api.do.spec.request_transformers == IsList( + dirty[JsonRPCMethod](method="do"), + req_transformer, + dirty[Url](original_template="/bar"), + check_order=False, + length=..., + ) + assert Api.do.spec.response_transformers == IsList( + res_transformer, + check_order=False, + length=..., + ) diff --git a/tests/http/test_aiohttp.py b/tests/http/test_aiohttp.py index 7b90b05..69a8174 100644 --- a/tests/http/test_aiohttp.py +++ b/tests/http/test_aiohttp.py @@ -43,13 +43,13 @@ async def test_jsonrpc_aiohttp(server_addr, session): jsonrpc = JsonRPCBuilder(url="jsonrpc") class Client(AiohttpClient): - @jsonrpc("good") + @jsonrpc(method="good") def do_good(self, body: Any) -> Any: ... - @jsonrpc("bad") + @jsonrpc(method="bad") def do_bad(self) -> Any: ... - @jsonrpc("invalid") + @jsonrpc(method="invalid") def do_invalid(self) -> Any: ... client = Client(server_addr, session) diff --git a/tests/http/test_httpx.py b/tests/http/test_httpx.py index 482ab45..e19f4de 100644 --- a/tests/http/test_httpx.py +++ b/tests/http/test_httpx.py @@ -77,13 +77,13 @@ async def test_jsonrpc_httpx_async(server_addr, async_session): jsonrpc = JsonRPCBuilder(url="jsonrpc") class Client(AsyncHttpxClient): - @jsonrpc("good") + @jsonrpc(method="good") def do_good(self, body: Any) -> Any: ... - @jsonrpc("bad") + @jsonrpc(method="bad") def do_bad(self) -> Any: ... - @jsonrpc("invalid") + @jsonrpc(method="invalid") def do_invalid(self) -> Any: ... client = Client(server_addr, async_session) diff --git a/tests/http/test_requests.py b/tests/http/test_requests.py index f71f619..acab837 100644 --- a/tests/http/test_requests.py +++ b/tests/http/test_requests.py @@ -39,13 +39,13 @@ def test_jsonrpc_requests(server_addr): jsonrpc = JsonRPCBuilder(url="jsonrpc") class Client(RequestsClient): - @jsonrpc("good") + @jsonrpc(method="good") def do_good(self, body: Any) -> Any: ... - @jsonrpc("bad") + @jsonrpc(method="bad") def do_bad(self) -> Any: ... - @jsonrpc("invalid") + @jsonrpc(method="invalid") def do_invalid(self) -> Any: ... client = Client(server_addr, requests.Session()) From 143138ed5d18a7bf6537bd737ed0932d794314ff Mon Sep 17 00:00:00 2001 From: Apostol Fet Date: Sat, 6 Dec 2025 16:12:57 +0300 Subject: [PATCH 2/2] feat(jsonrpc): method as first positional argument --- src/descanso/jsonrpc.py | 33 ++++++++++++++++----------------- tests/builders/test_jsonrpc.py | 6 +++--- tests/http/test_aiohttp.py | 6 +++--- tests/http/test_httpx.py | 6 +++--- tests/http/test_requests.py | 6 +++--- 5 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/descanso/jsonrpc.py b/src/descanso/jsonrpc.py index 49d78b6..df16775 100644 --- a/src/descanso/jsonrpc.py +++ b/src/descanso/jsonrpc.py @@ -359,15 +359,16 @@ def _add_default_response_transformers(self, spec: MethodSpec) -> None: ) def _add_default_jsonrpc_method(self, spec: MethodSpec) -> None: - jsonrpc_method_transformer = next( + jsonrpc_method_field = next( ( field - for field in spec.request_transformers - if isinstance(field, JsonRPCMethod) + for field in spec.fields_out + if field.name == EXTRA_JSON_RPC_METHOD + and field.dest is FieldDestination.EXTRA ), None, ) - if jsonrpc_method_transformer is None: + if jsonrpc_method_field is None: self._add_request_transformer(spec, JsonRPCMethod(spec.name)) @overload @@ -416,7 +417,7 @@ def __call__( @overload def __call__( self, - transformer: Transformer | None = None, + parameter: Transformer | str | None = None, *transformers: Transformer, method: str | None = None, **params: Unpack[BuilderParams], @@ -424,9 +425,8 @@ def __call__( def __call__( self, - func_or_transformer: Callable | Transformer | None = None, + func_or_parameter: Callable | Transformer | str | None = None, *transformers: Transformer, - method: str | None = None, **params: Unpack[BuilderParams], ) -> Any: if transformers or params: @@ -434,16 +434,15 @@ def __call__( else: instance = self - additional_transformers = ( - (JsonRPCMethod(method),) if method is not None else () - ) - - if func_or_transformer is None: - return instance.with_params(*additional_transformers) - elif isinstance(func_or_transformer, Transformer): + if func_or_parameter is None: + return instance + elif isinstance(func_or_parameter, str): + return instance.with_params( + JsonRPCMethod(func_or_parameter), + ) + elif isinstance(func_or_parameter, Transformer): return instance.with_params( - func_or_transformer, - *additional_transformers, + func_or_parameter, ) else: - return instance.decorate(func_or_transformer) + return instance.decorate(func_or_parameter) diff --git a/tests/builders/test_jsonrpc.py b/tests/builders/test_jsonrpc.py index c3bf00f..cf5e900 100644 --- a/tests/builders/test_jsonrpc.py +++ b/tests/builders/test_jsonrpc.py @@ -29,7 +29,7 @@ def test_url(): jsonrpc = JsonRPCBuilder(url="/foo") class Api: - @jsonrpc(method="methodname") + @jsonrpc("methodname") def do(self, body: int) -> Model: """Hello""" @@ -80,7 +80,7 @@ def test_params(): ) class Api: - @jsonrpc(method="methodname") + @jsonrpc("methodname") def do(self, data: int) -> Model: """Hello""" @@ -111,7 +111,7 @@ def test_override(): jsonrpc = JsonRPCBuilder(url="/foo") class Api: - @jsonrpc(method="methodname", url="/bar") + @jsonrpc("methodname", url="/bar") def do(self, data: int) -> Model: """Hello""" diff --git a/tests/http/test_aiohttp.py b/tests/http/test_aiohttp.py index 69a8174..7b90b05 100644 --- a/tests/http/test_aiohttp.py +++ b/tests/http/test_aiohttp.py @@ -43,13 +43,13 @@ async def test_jsonrpc_aiohttp(server_addr, session): jsonrpc = JsonRPCBuilder(url="jsonrpc") class Client(AiohttpClient): - @jsonrpc(method="good") + @jsonrpc("good") def do_good(self, body: Any) -> Any: ... - @jsonrpc(method="bad") + @jsonrpc("bad") def do_bad(self) -> Any: ... - @jsonrpc(method="invalid") + @jsonrpc("invalid") def do_invalid(self) -> Any: ... client = Client(server_addr, session) diff --git a/tests/http/test_httpx.py b/tests/http/test_httpx.py index e19f4de..482ab45 100644 --- a/tests/http/test_httpx.py +++ b/tests/http/test_httpx.py @@ -77,13 +77,13 @@ async def test_jsonrpc_httpx_async(server_addr, async_session): jsonrpc = JsonRPCBuilder(url="jsonrpc") class Client(AsyncHttpxClient): - @jsonrpc(method="good") + @jsonrpc("good") def do_good(self, body: Any) -> Any: ... - @jsonrpc(method="bad") + @jsonrpc("bad") def do_bad(self) -> Any: ... - @jsonrpc(method="invalid") + @jsonrpc("invalid") def do_invalid(self) -> Any: ... client = Client(server_addr, async_session) diff --git a/tests/http/test_requests.py b/tests/http/test_requests.py index acab837..f71f619 100644 --- a/tests/http/test_requests.py +++ b/tests/http/test_requests.py @@ -39,13 +39,13 @@ def test_jsonrpc_requests(server_addr): jsonrpc = JsonRPCBuilder(url="jsonrpc") class Client(RequestsClient): - @jsonrpc(method="good") + @jsonrpc("good") def do_good(self, body: Any) -> Any: ... - @jsonrpc(method="bad") + @jsonrpc("bad") def do_bad(self) -> Any: ... - @jsonrpc(method="invalid") + @jsonrpc("invalid") def do_invalid(self) -> Any: ... client = Client(server_addr, requests.Session())