rpc-python-toolkit 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  from .client import RpcClient, RpcSafeClient
2
- from .endpoint import RpcEndpoint, RpcHttpResponse, RpcSafeEndpoint
2
+ from .endpoint import RpcCallContext, RpcEndpoint, RpcHttpResponse, RpcSafeEndpoint
3
3
  from .errors import RpcError, RpcRemoteError
4
4
  from .serialization import deserialize_value, serialize_value
5
5
  from .version import __version__
@@ -10,10 +10,10 @@ __all__ = [
10
10
  "serialize_value",
11
11
  "RpcClient",
12
12
  "RpcEndpoint",
13
+ "RpcCallContext",
13
14
  "RpcError",
14
15
  "RpcHttpResponse",
15
16
  "RpcRemoteError",
16
17
  "RpcSafeClient",
17
18
  "RpcSafeEndpoint",
18
19
  ]
19
-
@@ -23,6 +23,19 @@ class RpcHttpResponse:
23
23
  body: Any | None = None
24
24
 
25
25
 
26
+ @dataclass(frozen=True)
27
+ class RpcCallContext:
28
+ """Context used when application state is not a mapping.
29
+
30
+ Mapping application contexts are merged with request data for backward
31
+ compatibility. For other application context types, this wrapper keeps
32
+ application and request scopes explicit.
33
+ """
34
+
35
+ application: Any
36
+ request: dict[str, Any]
37
+
38
+
26
39
  @dataclass
27
40
  class MethodConfig:
28
41
  handler: Handler
@@ -40,6 +53,8 @@ class RpcEndpoint:
40
53
  strict_mode: bool = True,
41
54
  enable_introspection: bool = False,
42
55
  introspection_prefix: str = "__rpc",
56
+ enable_batch: bool = True,
57
+ max_batch_size: int = 100,
43
58
  max_serialization_depth: int = 100,
44
59
  max_deserialization_depth: int = 100,
45
60
  ):
@@ -49,6 +64,8 @@ class RpcEndpoint:
49
64
  self.strict_mode = strict_mode
50
65
  self.enable_introspection = enable_introspection
51
66
  self.introspection_prefix = introspection_prefix
67
+ self.enable_batch = enable_batch
68
+ self.max_batch_size = max_batch_size
52
69
  self.max_serialization_depth = max_serialization_depth
53
70
  self.max_deserialization_depth = max_deserialization_depth
54
71
  self.validator = SchemaValidator()
@@ -159,6 +176,29 @@ class RpcEndpoint:
159
176
  make_response(None, error={"code": -32600, "message": "Invalid Request"}),
160
177
  )
161
178
 
179
+ if not self.enable_batch:
180
+ return RpcHttpResponse(
181
+ 200,
182
+ headers,
183
+ make_response(
184
+ None,
185
+ error={"code": -32600, "message": "Batch requests are not enabled"},
186
+ ),
187
+ )
188
+
189
+ if self.max_batch_size > 0 and len(batch) > self.max_batch_size:
190
+ return RpcHttpResponse(
191
+ 200,
192
+ headers,
193
+ make_response(
194
+ None,
195
+ error={
196
+ "code": -32600,
197
+ "message": f"Batch size exceeds maximum of {self.max_batch_size}",
198
+ },
199
+ ),
200
+ )
201
+
162
202
  responses = []
163
203
  for item in batch:
164
204
  result = self._process_single(item, context)
@@ -226,11 +266,23 @@ class RpcEndpoint:
226
266
  context: dict[str, Any],
227
267
  params: Any,
228
268
  ) -> Any:
229
- result = handler(request, self.context if self.context is not None else context, params)
269
+ result = handler(request, self._resolve_handler_context(context), params)
230
270
  if inspect.isawaitable(result):
231
271
  raise RpcError("Async handlers require an async adapter", -32603)
232
272
  return result
233
273
 
274
+ def _resolve_handler_context(self, request_context: dict[str, Any]) -> Any:
275
+ if self.context is None:
276
+ return request_context
277
+
278
+ if isinstance(self.context, dict):
279
+ return {**self.context, **request_context}
280
+
281
+ if request_context:
282
+ return RpcCallContext(application=self.context, request=request_context)
283
+
284
+ return self.context
285
+
234
286
  def _register_introspection_methods(self) -> None:
235
287
  prefix = self.introspection_prefix
236
288
 
@@ -255,7 +307,7 @@ class RpcEndpoint:
255
307
  f"{prefix}.capabilities",
256
308
  lambda _request, _context, _params: {
257
309
  "jsonrpc": "2.0",
258
- "batch": True,
310
+ "batch": self.enable_batch,
259
311
  "notifications": True,
260
312
  "introspection": True,
261
313
  "safeMode": self.safe_enabled,
@@ -311,4 +363,3 @@ class RpcSafeEndpoint(RpcEndpoint):
311
363
  options.setdefault("safe_enabled", True)
312
364
  options.setdefault("strict_mode", True)
313
365
  super().__init__(context, **options)
314
-
@@ -1,2 +1 @@
1
- __version__ = "0.1.0"
2
-
1
+ __version__ = "0.2.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rpc-python-toolkit
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Framework-agnostic Python JSON-RPC 2.0 toolkit with Safe Mode interoperability.
5
5
  Author: Nicola Carpanese
6
6
  License-Expression: MIT
@@ -25,13 +25,19 @@ Dynamic: license-file
25
25
 
26
26
  # RPC Python Toolkit
27
27
 
28
+ [![CI](https://github.com/n-car/rpc-python-toolkit/actions/workflows/ci.yml/badge.svg)](https://github.com/n-car/rpc-python-toolkit/actions/workflows/ci.yml)
29
+ [![PyPI Version](https://img.shields.io/pypi/v/rpc-python-toolkit.svg)](https://pypi.org/project/rpc-python-toolkit/)
30
+ [![Python Versions](https://img.shields.io/pypi/pyversions/rpc-python-toolkit.svg)](https://pypi.org/project/rpc-python-toolkit/)
31
+ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
32
+ [![Status](https://img.shields.io/badge/status-alpha-orange.svg)](https://pypi.org/project/rpc-python-toolkit/)
33
+
28
34
  Framework-agnostic JSON-RPC 2.0 toolkit for Python.
29
35
 
30
36
  Use this package when you need a small Python JSON-RPC 2.0 client/server core that can be embedded directly or adapted to HTTP frameworks. It follows the same protocol conventions used across the RPC Toolkit ecosystem.
31
37
 
32
38
  ## Project Status
33
39
 
34
- Small early package with the core JSON-RPC behavior implemented.
40
+ Alpha package with the core JSON-RPC behavior implemented.
35
41
 
36
42
  - Published on PyPI as `rpc-python-toolkit`.
37
43
  - Standard JSON-RPC 2.0 remains the default behavior.
@@ -80,7 +86,11 @@ python -m unittest discover
80
86
  ```python
81
87
  from rpc_python_toolkit import RpcEndpoint
82
88
 
83
- rpc = RpcEndpoint(enable_introspection=True)
89
+ rpc = RpcEndpoint(
90
+ enable_introspection=True,
91
+ enable_batch=True,
92
+ max_batch_size=100,
93
+ )
84
94
 
85
95
  def test(request, context, params):
86
96
  return {"ok": True, "params": params}
@@ -97,6 +107,12 @@ response = rpc.handle_payload({
97
107
  print(response.body)
98
108
  ```
99
109
 
110
+ Batch can be disabled with `enable_batch=False`. Introspection remains opt-in.
111
+ When both application and request contexts are dictionaries, handlers receive a
112
+ merged dictionary and request values take precedence. For a non-dictionary
113
+ application context plus request data, handlers receive `RpcCallContext` with
114
+ separate `application` and `request` attributes.
115
+
100
116
  Result:
101
117
 
102
118
  ```json
@@ -0,0 +1,13 @@
1
+ rpc_python_toolkit/__init__.py,sha256=OR3yfeiE-KEwC6CqAql59PljqW_VzHDyTwCZ5h7iJwU,514
2
+ rpc_python_toolkit/client.py,sha256=QD3vf8OSdDe42522nwWEy2gEyNAjNJzayezvqN6jP5A,5276
3
+ rpc_python_toolkit/endpoint.py,sha256=uTIM2YxiOb6lk4ORMCyg12T64CERFuQWXCjQSd8ditc,12498
4
+ rpc_python_toolkit/errors.py,sha256=en8mmO56IeT8OfRCTheMxNOUN09RpdLqR8X4BQTB6gk,374
5
+ rpc_python_toolkit/protocol.py,sha256=MMQ1tF1waizZ0dlq3LZc_Dx9tVN00KFLaWh70wR9g7A,1145
6
+ rpc_python_toolkit/serialization.py,sha256=gWI0UyxzNj-EATaBkSG9i-b-De-bH49igqBmzkfE9fM,5755
7
+ rpc_python_toolkit/validation.py,sha256=MXibLokBpBq_mDo7yxZ_64S6Mc_S1LWPM6UYsdxTXZg,617
8
+ rpc_python_toolkit/version.py,sha256=Zn1KFblwuFHiDRdRAiRnDBRkbPttWh44jKa5zG2ov0E,22
9
+ rpc_python_toolkit-0.2.0.dist-info/licenses/LICENSE,sha256=-JOXJbeUglpNN6FaH-5OkUacdU1e8cXsttGJxO8EAAY,1074
10
+ rpc_python_toolkit-0.2.0.dist-info/METADATA,sha256=eCYHtsYntC9qSy6AxvDv8BLf8-vVlcnbC_mK9MciTiA,6319
11
+ rpc_python_toolkit-0.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
12
+ rpc_python_toolkit-0.2.0.dist-info/top_level.txt,sha256=KeHhMmqjDgfOlP13qPd9_FhW3vXFe2xTL3HzuDAqiv4,19
13
+ rpc_python_toolkit-0.2.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.1)
2
+ Generator: setuptools (84.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,13 +0,0 @@
1
- rpc_python_toolkit/__init__.py,sha256=ph_tY5IvR-ihwa9ubr-vKWIhS4ru73RmHVbRjt-7YME,477
2
- rpc_python_toolkit/client.py,sha256=QD3vf8OSdDe42522nwWEy2gEyNAjNJzayezvqN6jP5A,5276
3
- rpc_python_toolkit/endpoint.py,sha256=vMizSI0lQH2HpocWyZS4238NE2wK2peQmkjSvYArVXo,10867
4
- rpc_python_toolkit/errors.py,sha256=en8mmO56IeT8OfRCTheMxNOUN09RpdLqR8X4BQTB6gk,374
5
- rpc_python_toolkit/protocol.py,sha256=MMQ1tF1waizZ0dlq3LZc_Dx9tVN00KFLaWh70wR9g7A,1145
6
- rpc_python_toolkit/serialization.py,sha256=gWI0UyxzNj-EATaBkSG9i-b-De-bH49igqBmzkfE9fM,5755
7
- rpc_python_toolkit/validation.py,sha256=MXibLokBpBq_mDo7yxZ_64S6Mc_S1LWPM6UYsdxTXZg,617
8
- rpc_python_toolkit/version.py,sha256=p4MxVO8ih2-hnh7INVBkrqMKBDGTkMso1LEbM6mx3jg,23
9
- rpc_python_toolkit-0.1.0.dist-info/licenses/LICENSE,sha256=-JOXJbeUglpNN6FaH-5OkUacdU1e8cXsttGJxO8EAAY,1074
10
- rpc_python_toolkit-0.1.0.dist-info/METADATA,sha256=6SM4BQFhVt9QUCPJmp3k20qKy5Zcugd0hEhYQPPkm08,5317
11
- rpc_python_toolkit-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
12
- rpc_python_toolkit-0.1.0.dist-info/top_level.txt,sha256=KeHhMmqjDgfOlP13qPd9_FhW3vXFe2xTL3HzuDAqiv4,19
13
- rpc_python_toolkit-0.1.0.dist-info/RECORD,,