0
# Method Dispatching
1
2
Flexible method registration and dispatch system supporting functions, classes, objects, decorators, and context injection. The Dispatcher class provides a dictionary-like interface for mapping method names to callable functions with extensive customization options.
3
4
## Capabilities
5
6
### Core Dispatcher
7
8
Dictionary-like object that maps method names to callable functions, implementing the MutableMapping interface for standard dictionary operations.
9
10
```python { .api }
11
class Dispatcher:
12
def __init__(self, prototype = None):
13
"""
14
Initialize dispatcher with optional prototype.
15
16
Parameters:
17
- prototype: Initial method mapping (dict or object with callable attributes)
18
"""
19
20
# Dictionary interface
21
def __getitem__(self, key: str):
22
"""Get method by name."""
23
24
def __setitem__(self, key: str, value):
25
"""Set method by name."""
26
27
def __delitem__(self, key: str):
28
"""Remove method by name."""
29
30
def __len__(self) -> int:
31
"""Number of registered methods."""
32
33
def __iter__(self):
34
"""Iterate over method names."""
35
36
def __repr__(self) -> str:
37
"""String representation of method mapping."""
38
39
# Properties
40
method_map: dict # Internal method storage
41
context_arg_for_method: dict # Context argument mapping
42
```
43
44
### Method Registration
45
46
Multiple ways to register methods with flexible naming and context injection support.
47
48
```python { .api }
49
def add_method(self, f = None, name: str = None, context_arg: str = None):
50
"""
51
Add a method to the dispatcher.
52
53
Parameters:
54
- f: Callable to register
55
- name: Method name (defaults to function name)
56
- context_arg: Parameter name that will receive context data
57
58
Returns:
59
The original function (for decorator usage)
60
61
Usage:
62
- As method: dispatcher.add_method(func, "name")
63
- As decorator: @dispatcher.add_method
64
- With custom name: @dispatcher.add_method(name="custom")
65
- With context: @dispatcher.add_method(context_arg="ctx")
66
"""
67
```
68
69
### Bulk Registration
70
71
Register multiple methods from classes, objects, or dictionaries with optional prefixing.
72
73
```python { .api }
74
def add_class(self, cls):
75
"""
76
Add all public methods from a class.
77
78
Parameters:
79
- cls: Class to register methods from
80
81
Methods are prefixed with lowercase class name + '.'
82
"""
83
84
def add_object(self, obj):
85
"""
86
Add all public methods from an object instance.
87
88
Parameters:
89
- obj: Object instance to register methods from
90
91
Methods are prefixed with lowercase class name + '.'
92
"""
93
94
def add_dict(self, dict: dict, prefix: str = ''):
95
"""
96
Add methods from dictionary.
97
98
Parameters:
99
- dict: Dictionary of name -> callable mappings
100
- prefix: Optional prefix for method names
101
"""
102
103
def build_method_map(self, prototype, prefix: str = ''):
104
"""
105
Build method map from prototype object or dictionary.
106
107
Parameters:
108
- prototype: Object or dict to extract methods from
109
- prefix: Optional prefix for method names
110
"""
111
```
112
113
### Global Dispatcher Instance
114
115
Pre-created dispatcher instance available for immediate use.
116
117
```python { .api }
118
# Global dispatcher instance
119
dispatcher: Dispatcher
120
```
121
122
## Usage Examples
123
124
### Basic Method Registration
125
126
```python
127
from jsonrpc import dispatcher, JSONRPCResponseManager
128
129
# Method 1: Using decorator
130
@dispatcher.add_method
131
def add(a, b):
132
"""Add two numbers."""
133
return a + b
134
135
@dispatcher.add_method
136
def multiply(x, y):
137
"""Multiply two numbers."""
138
return x * y
139
140
# Method 2: Direct registration
141
def subtract(a, b):
142
return a - b
143
144
dispatcher.add_method(subtract)
145
146
# Method 3: Dictionary-style assignment
147
dispatcher["divide"] = lambda a, b: a / b
148
149
# Test the methods
150
request = '{"jsonrpc": "2.0", "method": "add", "params": [5, 3], "id": 1}'
151
response = JSONRPCResponseManager.handle(request, dispatcher)
152
print(response.json)
153
# {"jsonrpc": "2.0", "result": 8, "id": 1}
154
```
155
156
### Custom Method Names
157
158
```python
159
from jsonrpc import Dispatcher
160
161
dispatcher = Dispatcher()
162
163
# Custom name with decorator
164
@dispatcher.add_method(name="math.add")
165
def addition(a, b):
166
return a + b
167
168
# Custom name with method call
169
def multiplication(a, b):
170
return a * b
171
172
dispatcher.add_method(multiplication, name="math.multiply")
173
174
# Dictionary-style with custom name
175
dispatcher["math.power"] = lambda base, exp: base ** exp
176
177
# Usage
178
request = '{"jsonrpc": "2.0", "method": "math.add", "params": [2, 3], "id": 1}'
179
response = JSONRPCResponseManager.handle(request, dispatcher)
180
print(response.json)
181
# {"jsonrpc": "2.0", "result": 5, "id": 1}
182
```
183
184
### Context Injection
185
186
```python
187
from jsonrpc import Dispatcher, JSONRPCResponseManager
188
189
dispatcher = Dispatcher()
190
191
# Method that receives context
192
@dispatcher.add_method(context_arg="context")
193
def get_request_info(context):
194
"""Return information about the current request."""
195
request = context.get("request")
196
return {
197
"request_id": request._id if request else None,
198
"method": request.method if request else None,
199
"user": context.get("user", "anonymous")
200
}
201
202
@dispatcher.add_method(context_arg="ctx")
203
def calculate_with_audit(a, b, operation, ctx):
204
"""Perform calculation with audit logging."""
205
result = a + b if operation == "add" else a * b
206
207
# Access context data
208
user = ctx.get("user", "unknown")
209
timestamp = ctx.get("timestamp")
210
211
return {
212
"result": result,
213
"calculated_by": user,
214
"timestamp": timestamp
215
}
216
217
# Handle request with context
218
context = {
219
"user": "alice",
220
"timestamp": "2023-01-01T12:00:00Z",
221
"session_id": "abc123"
222
}
223
224
request = '{"jsonrpc": "2.0", "method": "get_request_info", "id": 1}'
225
response = JSONRPCResponseManager.handle(request, dispatcher, context)
226
print(response.json)
227
# {"jsonrpc": "2.0", "result": {"request_id": 1, "method": "get_request_info", "user": "alice"}, "id": 1}
228
```
229
230
### Class and Object Registration
231
232
```python
233
from jsonrpc import Dispatcher
234
235
# Example service class
236
class MathService:
237
def add(self, a, b):
238
return a + b
239
240
def subtract(self, a, b):
241
return a - b
242
243
def _private_method(self):
244
# Private methods (starting with _) are not registered
245
return "private"
246
247
class StringService:
248
def __init__(self, prefix=""):
249
self.prefix = prefix
250
251
def upper(self, text):
252
return (self.prefix + text).upper()
253
254
def lower(self, text):
255
return (self.prefix + text).lower()
256
257
dispatcher = Dispatcher()
258
259
# Register all public methods from class (creates new instance)
260
dispatcher.add_class(MathService)
261
262
# Register methods from specific object instance
263
string_service = StringService("Hello ")
264
dispatcher.add_object(string_service)
265
266
# Methods are prefixed with class name
267
print(list(dispatcher.method_map.keys()))
268
# ['mathservice.add', 'mathservice.subtract', 'stringservice.upper', 'stringservice.lower']
269
270
# Usage
271
request = '{"jsonrpc": "2.0", "method": "mathservice.add", "params": [10, 5], "id": 1}'
272
response = JSONRPCResponseManager.handle(request, dispatcher)
273
print(response.json)
274
# {"jsonrpc": "2.0", "result": 15, "id": 1}
275
```
276
277
### Dictionary Registration
278
279
```python
280
from jsonrpc import Dispatcher
281
282
# Create method dictionary
283
math_methods = {
284
"add": lambda a, b: a + b,
285
"multiply": lambda a, b: a * b,
286
"power": lambda base, exp: base ** exp
287
}
288
289
string_methods = {
290
"upper": str.upper,
291
"lower": str.lower,
292
"capitalize": str.capitalize
293
}
294
295
dispatcher = Dispatcher()
296
297
# Register with prefix
298
dispatcher.add_dict(math_methods, prefix="math")
299
dispatcher.add_dict(string_methods, prefix="string")
300
301
print(list(dispatcher.method_map.keys()))
302
# ['math.add', 'math.multiply', 'math.power', 'string.upper', 'string.lower', 'string.capitalize']
303
304
# Usage
305
request = '{"jsonrpc": "2.0", "method": "string.upper", "params": ["hello world"], "id": 1}'
306
response = JSONRPCResponseManager.handle(request, dispatcher)
307
print(response.json)
308
# {"jsonrpc": "2.0", "result": "HELLO WORLD", "id": 1}
309
```
310
311
### Prototype Initialization
312
313
```python
314
from jsonrpc import Dispatcher
315
316
# Initialize with function dictionary
317
initial_methods = {
318
"ping": lambda: "pong",
319
"echo": lambda msg: msg,
320
"add": lambda a, b: a + b
321
}
322
323
dispatcher = Dispatcher(prototype=initial_methods)
324
325
# Initialize with object
326
class APIService:
327
def status(self):
328
return "running"
329
330
def version(self):
331
return "1.0.0"
332
333
api_dispatcher = Dispatcher(prototype=APIService())
334
335
print(list(api_dispatcher.method_map.keys()))
336
# ['status', 'version']
337
338
# Add more methods later
339
@api_dispatcher.add_method
340
def shutdown():
341
return "shutting down"
342
```
343
344
### Method Removal and Management
345
346
```python
347
from jsonrpc import Dispatcher
348
349
dispatcher = Dispatcher()
350
351
# Add methods
352
dispatcher["add"] = lambda a, b: a + b
353
dispatcher["subtract"] = lambda a, b: a - b
354
dispatcher["multiply"] = lambda a, b: a * b
355
356
print(len(dispatcher)) # 3
357
print("add" in dispatcher) # True
358
359
# Remove method
360
del dispatcher["subtract"]
361
print(len(dispatcher)) # 2
362
363
# Clear all methods
364
dispatcher.method_map.clear()
365
print(len(dispatcher)) # 0
366
367
# Check available methods
368
@dispatcher.add_method
369
def test():
370
return "test"
371
372
print(list(dispatcher)) # ['test']
373
print(repr(dispatcher)) # {'test': <function test at 0x...>}
374
```