15
15
from types import SimpleNamespace
16
16
from typing import (
17
17
Any ,
18
+ Callable ,
18
19
Dict ,
19
20
Generator ,
20
21
Iterable ,
21
22
List ,
23
+ NamedTuple ,
22
24
Optional ,
23
25
Set ,
24
26
Tuple ,
25
27
Union ,
26
- Callable ,
27
28
)
28
29
29
30
from apply_defaults import apply_config # type: ignore
33
34
34
35
from .log import log_
35
36
from .methods import Method , Methods , global_methods , validate_args , lookup
36
- from .request import NOCONTEXT , Request
37
+ from .request import Request , is_notification , NOID
37
38
from .response import (
38
39
ApiErrorResponse ,
39
40
BatchResponse ,
63
64
config = ConfigParser (default_section = "dispatch" )
64
65
config .read ([".jsonrpcserverrc" , os .path .expanduser ("~/.jsonrpcserverrc" )])
65
66
67
+ Context = NamedTuple (
68
+ "Context" ,
69
+ [("request" , Request ), ("extra" , Any )],
70
+ )
71
+
66
72
67
73
def add_handlers () -> Tuple [logging .Handler , logging .Handler ]:
68
74
# Request handler
@@ -159,12 +165,12 @@ def handle_exceptions(request: Request, debug: bool) -> Generator:
159
165
logging .exception (exc )
160
166
handler .response = ExceptionResponse (exc , id = request .id , debug = debug )
161
167
finally :
162
- if request . is_notification :
168
+ if is_notification ( request ) :
163
169
handler .response = NotificationResponse ()
164
170
165
171
166
172
def safe_call (
167
- request : Request , methods : Methods , * , debug : bool , serialize : Callable
173
+ request : Request , methods : Methods , * , debug : bool , extra : Any , serialize : Callable
168
174
) -> Response :
169
175
"""
170
176
Call a Request, catching exceptions to ensure we always return a Response.
@@ -179,7 +185,17 @@ def safe_call(
179
185
A Response object.
180
186
"""
181
187
with handle_exceptions (request , debug ) as handler :
182
- result = call (lookup (methods , request .method ), * request .args , ** request .kwargs )
188
+ if isinstance (request .params , list ):
189
+ result = call (
190
+ lookup (methods , request .method ),
191
+ * ([Context (request = request , extra = extra )] + request .params ),
192
+ )
193
+ else :
194
+ result = call (
195
+ lookup (methods , request .method ),
196
+ Context (request = request , extra = extra ),
197
+ ** request .params ,
198
+ )
183
199
# Ensure value returned from the method is JSON-serializable. If not,
184
200
# handle_exception will set handler.response to an ExceptionResponse
185
201
serialize (result )
@@ -192,6 +208,8 @@ def safe_call(
192
208
def call_requests (
193
209
requests : Union [Request , Iterable [Request ]],
194
210
methods : Methods ,
211
+ * ,
212
+ extra : Any ,
195
213
debug : bool ,
196
214
serialize : Callable ,
197
215
) -> Response :
@@ -204,44 +222,59 @@ def call_requests(
204
222
debug: Include more information in error responses.
205
223
serialize: Function that is used to serialize data.
206
224
"""
207
- if isinstance (requests , Iterable ):
208
- return BatchResponse (
209
- [safe_call (r , methods , debug = debug , serialize = serialize ) for r in requests ],
225
+ return (
226
+ BatchResponse (
227
+ [
228
+ safe_call (
229
+ r , methods = methods , debug = debug , extra = extra , serialize = serialize
230
+ )
231
+ for r in requests
232
+ ],
210
233
serialize_func = serialize ,
211
234
)
212
- return safe_call (requests , methods , debug = debug , serialize = serialize )
235
+ if isinstance (requests , list )
236
+ else safe_call (
237
+ requests , methods = methods , debug = debug , extra = extra , serialize = serialize
238
+ )
239
+ )
213
240
214
241
215
242
def create_requests (
216
- requests : Union [Dict , List ], * , context : Any = NOCONTEXT , convert_camel_case : bool
243
+ requests : Union [Dict , List [ Dict ]],
217
244
) -> Union [Request , Set [Request ]]:
218
245
"""
219
- Create a Request object from a dictionary (or list of them ).
246
+ Converts a raw deserialized request dictionary to a Request (namedtuple ).
220
247
221
248
Args:
222
- requests: Request object, or a collection of them.
223
- methods: The list of methods that can be called.
224
- context: If specified, will be the first positional argument in all requests.
225
- convert_camel_case: Will convert the method name/any named params to snake case.
249
+ requests: Request dict, or a list of dicts.
226
250
227
251
Returns:
228
- A Request object, or a collection of them.
252
+ A Request object, or a list of them.
229
253
"""
230
- if isinstance (requests , list ):
231
- return {
232
- Request (context = context , convert_camel_case = convert_camel_case , ** request )
254
+ return (
255
+ [
256
+ Request (
257
+ method = request ["method" ],
258
+ params = request .get ("params" , []),
259
+ id = request .get ("id" , NOID ),
260
+ )
233
261
for request in requests
234
- }
235
- return Request (context = context , convert_camel_case = convert_camel_case , ** requests )
262
+ ]
263
+ if isinstance (requests , list )
264
+ else Request (
265
+ method = requests ["method" ],
266
+ params = requests .get ("params" , []),
267
+ id = requests .get ("id" , NOID ),
268
+ )
269
+ )
236
270
237
271
238
272
def dispatch_pure (
239
273
request : str ,
240
274
methods : Methods ,
241
275
* ,
242
- context : Any ,
243
- convert_camel_case : bool ,
244
276
debug : bool ,
277
+ extra : Any ,
245
278
serialize : Callable ,
246
279
deserialize : Callable ,
247
280
) -> Response :
@@ -255,9 +288,8 @@ def dispatch_pure(
255
288
Args:
256
289
request: The incoming request string.
257
290
methods: Collection of methods that can be called.
258
- context: If specified, will be the first positional argument in all requests.
259
- convert_camel_case: Will convert the method name/any named params to snake case.
260
291
debug: Include more information in error responses.
292
+ extra: Will be included in the context dictionary passed to methods.
261
293
serialize: Function that is used to serialize data.
262
294
deserialize: Function that is used to deserialize data.
263
295
Returns:
@@ -270,10 +302,9 @@ def dispatch_pure(
270
302
except ValidationError as exc :
271
303
return InvalidJSONRPCResponse (data = None , debug = debug )
272
304
return call_requests (
273
- create_requests (
274
- deserialized , context = context , convert_camel_case = convert_camel_case
275
- ),
276
- methods ,
305
+ create_requests (deserialized ),
306
+ methods = methods ,
307
+ extra = extra ,
277
308
debug = debug ,
278
309
serialize = serialize ,
279
310
)
@@ -285,8 +316,7 @@ def dispatch(
285
316
methods : Optional [Methods ] = None ,
286
317
* ,
287
318
basic_logging : bool = False ,
288
- convert_camel_case : bool = False ,
289
- context : Any = NOCONTEXT ,
319
+ extra : Optional [dict ] = None ,
290
320
debug : bool = False ,
291
321
trim_log_values : bool = False ,
292
322
serialize : Callable = default_serialize ,
@@ -303,9 +333,7 @@ def dispatch(
303
333
request: The incoming request string.
304
334
methods: Collection of methods that can be called. If not passed, uses the
305
335
internal methods object.
306
- context: If specified, will be the first positional argument in all requests.
307
- convert_camel_case: Convert keys in params dictionary from camel case to snake
308
- case.
336
+ extra: Extra data available inside methods (as context.extra).
309
337
debug: Include more information in error responses.
310
338
trim_log_values: Show abbreviated requests and responses in log.
311
339
serialize: Function that is used to serialize data.
@@ -327,8 +355,7 @@ def dispatch(
327
355
request ,
328
356
methods ,
329
357
debug = debug ,
330
- context = context ,
331
- convert_camel_case = convert_camel_case ,
358
+ extra = extra ,
332
359
serialize = serialize ,
333
360
deserialize = deserialize ,
334
361
)
0 commit comments