Import upstream version 0.4.2+git20201109.1.7b3659d
Debian Janitor
2 years ago
0 | Metadata-Version: 2.1 | |
1 | Name: jsonrpclib-pelix | |
2 | Version: 0.4.2 | |
3 | Summary: This project is an implementation of the JSON-RPC v2.0 specification (backwards-compatible) as a client library, for Python 2.7 and Python 3. This version is a fork of jsonrpclib by Josh Marshall, made to be also usable with Pelix/iPOPO remote services. | |
4 | Home-page: http://github.com/tcalmant/jsonrpclib/ | |
5 | Author: Thomas Calmant | |
6 | Author-email: thomas.calmant+github@gmail.com | |
7 | License: Apache License 2.0 | |
8 | Description: # JSONRPClib (patched for Pelix and Python 3) | |
9 | ||
10 | [![Latest Version](https://img.shields.io/pypi/v/jsonrpclib-pelix.svg)](https://pypi.python.org/pypi/jsonrpclib-pelix/) | |
11 | [![License](https://img.shields.io/pypi/l/jsonrpclib-pelix.svg)](https://pypi.python.org/pypi/jsonrpclib-pelix/) | |
12 | [![Travis-CI status](https://travis-ci.org/tcalmant/jsonrpclib.svg?branch=master)](https://travis-ci.org/tcalmant/jsonrpclib) | |
13 | [![Coveralls status](https://coveralls.io/repos/tcalmant/jsonrpclib/badge.svg?branch=master)](https://coveralls.io/r/tcalmant/jsonrpclib?branch=master) | |
14 | ||
15 | This library is an implementation of the JSON-RPC specification. | |
16 | It supports both the original 1.0 specification, as well as the new | |
17 | (proposed) 2.0 specification, which includes batch submission, keyword | |
18 | arguments, etc. | |
19 | ||
20 | This library is licensed under the terms of the | |
21 | [Apache Software License 2.0](<http://www.apache.org/licenses/LICENSE-2.0.html>). | |
22 | ||
23 | ||
24 | ## About this version | |
25 | ||
26 | This is a patched version of the original `jsonrpclib` project by Josh Marshall, | |
27 | available at | |
28 | [joshmarshall/jsonrpclib](<https://github.com/joshmarshall/jsonrpclib>). | |
29 | ||
30 | The suffix *-pelix* only indicates that this version works with Pelix | |
31 | Remote Services, but it is **not** a Pelix specific implementation. | |
32 | ||
33 | * This version adds support for Python 3, staying compatible with Python 2.7. | |
34 | The support for Python 2.6 has been dropped, as it was becoming to hard to | |
35 | maintain. | |
36 | * It is now possible to use the `dispatch_method` argument while extending the | |
37 | `SimpleJSONRPCDispatcher`, to use a custom dispatcher. | |
38 | This allows to use this package by Pelix Remote Services. | |
39 | * It can use thread pools to control the number of threads spawned to handle | |
40 | notification requests and clients connections. | |
41 | * The modifications added in other forks of this project have been added: | |
42 | * From [drdaeman/jsonrpclib](<https://github.com/drdaeman/jsonrpclib>): | |
43 | * Improved JSON-RPC 1.0 support | |
44 | * Less strict error response handling | |
45 | * From [tuomassalo/jsonrpclib](<https://github.com/tuomassalo/jsonrpclib>): | |
46 | * In case of a non-predefined error, raise an AppError and give access | |
47 | to *error.data* | |
48 | * From [dejw/jsonrpclib](<https://github.com/dejw/jsonrpclib>): | |
49 | * Custom headers can be sent with request and associated tests | |
50 | * Since version 0.4, this package added back the support of Unix sockets. | |
51 | * This package cannot be installed with the original `jsonrpclib`, as it uses | |
52 | the same name. | |
53 | ||
54 | ## Summary | |
55 | ||
56 | This library implements the JSON-RPC 2.0 proposed specification in pure Python. | |
57 | It is designed to be as compatible with the syntax of `xmlrpclib` as possible | |
58 | (it extends where possible), so that projects using `xmlrpclib` could easily be | |
59 | modified to use JSON and experiment with the differences. | |
60 | ||
61 | It is backwards-compatible with the 1.0 specification, and supports all of the | |
62 | new proposed features of 2.0, including: | |
63 | ||
64 | - Batch submission (via the `MultiCall` class) | |
65 | - Keyword arguments | |
66 | - Notifications (both in a batch and 'normal') | |
67 | - Class translation using the `__jsonclass__` key. | |
68 | ||
69 | A `SimpleJSONRPCServer` class has been added. It is intended to emulate the | |
70 | `SimpleXMLRPCServer` from the default Python distribution. | |
71 | ||
72 | ## Requirements | |
73 | ||
74 | This library supports `cjson` and `simplejson`, and looks for the parsers in | |
75 | that order (searching first for `cjson`, then for the *built-in* `json` in 2.7+, | |
76 | and then the `simplejson` external library). | |
77 | One of these must be installed to use this library, although if you have a | |
78 | standard distribution of 2.7+, you should already have one. | |
79 | Keep in mind that `cjson` is supposed to be the quickest, I believe, so if you | |
80 | are going for full-on optimization you may want to pick it up. | |
81 | ||
82 | ## Installation | |
83 | ||
84 | You can install this from PyPI with one of the following commands (`sudo` | |
85 | might be required): | |
86 | ||
87 | ``` | |
88 | # Global installation | |
89 | pip install jsonrpclib-pelix | |
90 | ||
91 | # Local installation | |
92 | pip install --user jsonrpclib-pelix | |
93 | ``` | |
94 | ||
95 | Alternatively, you can download the source from the GitHub repository at | |
96 | [tcalmant/jsonrpclib](http://github.com/tcalmant/jsonrpclib) and manually | |
97 | install it with the following commands: | |
98 | ||
99 | ``` | |
100 | git clone git://github.com/tcalmant/jsonrpclib.git | |
101 | cd jsonrpclib | |
102 | python setup.py install | |
103 | ``` | |
104 | ||
105 | ## A note on logging | |
106 | ||
107 | `jsonrpclib-pelix` uses the `logging` module from the standard Python | |
108 | library to trace warnings and errors, but doesn't set it up. | |
109 | As a result, you have to configure the Python logging to print out traces. | |
110 | ||
111 | The easiest way to do it is to add those lines at the beginning of your code: | |
112 | ```python | |
113 | import logging | |
114 | logging.basiConfig() | |
115 | ``` | |
116 | ||
117 | More information can be found in the | |
118 | [`logging` documentation page](https://docs.python.org/3/library/logging.html). | |
119 | ||
120 | ## `SimpleJSONRPCServer` | |
121 | ||
122 | This is identical in usage (or should be) to the `SimpleXMLRPCServer` in the | |
123 | Python standard library. | |
124 | Some of the differences in features are that it obviously supports notification, | |
125 | batch calls, class translation (if left on), etc. | |
126 | ||
127 | **Note:** The import line is slightly different from the regular | |
128 | `SimpleXMLRPCServer`, since the `SimpleJSONRPCServer` is provided by th | |
129 | `jsonrpclib` library. | |
130 | ||
131 | ```python | |
132 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
133 | ||
134 | server = SimpleJSONRPCServer(('localhost', 8080)) | |
135 | server.register_function(pow) | |
136 | server.register_function(lambda x,y: x+y, 'add') | |
137 | server.register_function(lambda x: x, 'ping') | |
138 | server.serve_forever() | |
139 | ``` | |
140 | ||
141 | To start protect the server with SSL, use the following snippet: | |
142 | ||
143 | ```python | |
144 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
145 | import ssl | |
146 | ||
147 | # Setup the SSL socket | |
148 | server = SimpleJSONRPCServer(('localhost', 8080), bind_and_activate=False) | |
149 | server.socket = ssl.wrap_socket(server.socket, certfile='server.pem', | |
150 | server_side=True) | |
151 | server.server_bind() | |
152 | server.server_activate() | |
153 | ||
154 | # ... register functions | |
155 | # Start the server | |
156 | server.serve_forever() | |
157 | ``` | |
158 | ||
159 | ### Notification Thread Pool | |
160 | ||
161 | By default, notification calls are handled in the request handling thread. | |
162 | It is possible to use a thread pool to handle them, by giving it to the server | |
163 | using the `set_notification_pool()` method: | |
164 | ||
165 | ```python | |
166 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
167 | from jsonrpclib.threadpool import ThreadPool | |
168 | ||
169 | # Setup the thread pool: between 0 and 10 threads | |
170 | pool = ThreadPool(max_threads=10, min_threads=0) | |
171 | ||
172 | # Don't forget to start it | |
173 | pool.start() | |
174 | ||
175 | # Setup the server | |
176 | server = SimpleJSONRPCServer(('localhost', 8080)) | |
177 | server.set_notification_pool(pool) | |
178 | ||
179 | # Register methods | |
180 | server.register_function(pow) | |
181 | server.register_function(lambda x,y: x+y, 'add') | |
182 | server.register_function(lambda x: x, 'ping') | |
183 | ||
184 | try: | |
185 | server.serve_forever() | |
186 | finally: | |
187 | # Stop the thread pool (let threads finish their current task) | |
188 | pool.stop() | |
189 | server.set_notification_pool(None) | |
190 | ``` | |
191 | ||
192 | ### Threaded server | |
193 | ||
194 | It is also possible to use a thread pool to handle clients requests, using the | |
195 | `PooledJSONRPCServer` class. | |
196 | By default, this class uses pool of 0 to 30 threads. | |
197 | A custom pool can be given with the `thread_pool` parameter of the class | |
198 | constructor. | |
199 | ||
200 | The notification pool and the request pool are different: by default, a server | |
201 | with a request pool doesn't have a notification pool. | |
202 | ||
203 | ```python | |
204 | from jsonrpclib.SimpleJSONRPCServer import PooledJSONRPCServer | |
205 | from jsonrpclib.threadpool import ThreadPool | |
206 | ||
207 | # Setup the notification and request pools | |
208 | nofif_pool = ThreadPool(max_threads=10, min_threads=0) | |
209 | request_pool = ThreadPool(max_threads=50, min_threads=10) | |
210 | ||
211 | # Don't forget to start them | |
212 | nofif_pool.start() | |
213 | request_pool.start() | |
214 | ||
215 | # Setup the server | |
216 | server = PooledJSONRPCServer(('localhost', 8080), thread_pool=request_pool) | |
217 | server.set_notification_pool(nofif_pool) | |
218 | ||
219 | # Register methods | |
220 | server.register_function(pow) | |
221 | server.register_function(lambda x,y: x+y, 'add') | |
222 | server.register_function(lambda x: x, 'ping') | |
223 | ||
224 | try: | |
225 | server.serve_forever() | |
226 | finally: | |
227 | # Stop the thread pools (let threads finish their current task) | |
228 | request_pool.stop() | |
229 | nofif_pool.stop() | |
230 | server.set_notification_pool(None) | |
231 | ``` | |
232 | ||
233 | ### Unix socket | |
234 | ||
235 | To start a server listening on a Unix socket, you will have to use the | |
236 | following snippet: | |
237 | ||
238 | ```python | |
239 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
240 | import os | |
241 | import socket | |
242 | ||
243 | # Set the path to the socket file | |
244 | socket_name = "/tmp/my_socket.socket" | |
245 | ||
246 | # Ensure that the file doesn't exist yet (or an error will be raised) | |
247 | if os.path.exists(socket_name): | |
248 | os.remove(socket_name) | |
249 | ||
250 | try: | |
251 | # Start the server, indicating the socket family | |
252 | # The server will force some flags when in Unix socket mode | |
253 | # (no log request, no reuse address, ...) | |
254 | srv = SimpleJSONRPCServer(socket_name, address_family=socket.AF_UNIX) | |
255 | ||
256 | # ... register methods to the server | |
257 | # Run the server | |
258 | srv.serve_forever() | |
259 | except KeyboardInterrupt: | |
260 | # Shutdown the server gracefully | |
261 | srv.shutdown() | |
262 | srv.server_close() | |
263 | finally: | |
264 | # You should clean up after the server stopped | |
265 | os.remove(socket_name) | |
266 | ``` | |
267 | ||
268 | This feature is tested on Linux during Travis-CI builds. It also has | |
269 | been tested on Windows Subsystem for Linux (WSL) on Windows 10 1809. | |
270 | ||
271 | This feature is not available on "pure" Windows, as it doesn't provide | |
272 | the `AF_UNIX` address family. | |
273 | ||
274 | ## Client Usage | |
275 | ||
276 | This is (obviously) taken from a console session. | |
277 | ||
278 | ```python | |
279 | >>> import jsonrpclib | |
280 | >>> server = jsonrpclib.ServerProxy('http://localhost:8080') | |
281 | >>> server.add(5,6) | |
282 | 11 | |
283 | >>> server.add(x=5, y=10) | |
284 | 15 | |
285 | >>> server._notify.add(5,6) | |
286 | # No result returned... | |
287 | >>> batch = jsonrpclib.MultiCall(server) | |
288 | >>> batch.add(5, 6) | |
289 | >>> batch.ping({'key':'value'}) | |
290 | >>> batch._notify.add(4, 30) | |
291 | >>> results = batch() | |
292 | >>> for result in results: | |
293 | >>> ... print(result) | |
294 | 11 | |
295 | {'key': 'value'} | |
296 | # Note that there are only two responses -- this is according to spec. | |
297 | ||
298 | # Clean up | |
299 | >>> server('close')() | |
300 | ||
301 | # Using client history | |
302 | >>> history = jsonrpclib.history.History() | |
303 | >>> server = jsonrpclib.ServerProxy('http://localhost:8080', history=history) | |
304 | >>> server.add(5,6) | |
305 | 11 | |
306 | >>> print(history.request) | |
307 | {"id": "f682b956-c8e1-4506-9db4-29fe8bc9fcaa", "jsonrpc": "2.0", | |
308 | "method": "add", "params": [5, 6]} | |
309 | >>> print(history.response) | |
310 | {"id": "f682b956-c8e1-4506-9db4-29fe8bc9fcaa", "jsonrpc": "2.0", | |
311 | "result": 11} | |
312 | ||
313 | # Clean up | |
314 | >>> server('close')() | |
315 | ``` | |
316 | ||
317 | If you need 1.0 functionality, there are a bunch of places you can pass | |
318 | that in, although the best is just to give a specific configuration to | |
319 | `jsonrpclib.ServerProxy`: | |
320 | ||
321 | ```python | |
322 | >>> import jsonrpclib | |
323 | >>> jsonrpclib.config.DEFAULT.version | |
324 | 2.0 | |
325 | >>> config = jsonrpclib.config.Config(version=1.0) | |
326 | >>> history = jsonrpclib.history.History() | |
327 | >>> server = jsonrpclib.ServerProxy('http://localhost:8080', config=config, | |
328 | history=history) | |
329 | >>> server.add(7, 10) | |
330 | 17 | |
331 | >>> print(history.request) | |
332 | {"id": "827b2923-5b37-49a5-8b36-e73920a16d32", | |
333 | "method": "add", "params": [7, 10]} | |
334 | >>> print(history.response) | |
335 | {"id": "827b2923-5b37-49a5-8b36-e73920a16d32", "error": null, "result": 17} | |
336 | >>> server('close')() | |
337 | ``` | |
338 | ||
339 | The equivalent `loads` and `dumps` functions also exist, although with | |
340 | minor modifications. | |
341 | The `dumps` arguments are almost identical, but it adds three arguments: | |
342 | `rpcid` for the `id` key, `version` to specify the JSON-RPC compatibility, | |
343 | and `notify` if it's a request that you want to be a notification. | |
344 | ||
345 | Additionally, the `loads` method does not return the params and method like | |
346 | `xmlrpclib`, but instead | |
347 | a.) parses for errors, raising ProtocolErrors, and | |
348 | b.) returns the entire structure of the request / response for manual parsing. | |
349 | ||
350 | ### Unix sockets | |
351 | ||
352 | To connect a JSON-RPC server over a Unix socket, you have to use a specific | |
353 | protocol: `unix+http`. | |
354 | ||
355 | When connecting to a Unix socket in the current working directory, you can use | |
356 | the following syntax: `unix+http://my.socket` | |
357 | ||
358 | When you need to give an absolute path you must use the path part of the URL, | |
359 | the host part will be ignored. For example, you can use this URL to indicate a | |
360 | Unix socket in `/var/lib/daemon.socket`: `unix+http://./var/lib/daemon.socket` | |
361 | ||
362 | **Note:** Currently, only HTTP is supported over a Unix socket. | |
363 | If you want HTTPS support to be implemented, please create an | |
364 | [issue on GitHub](https://github.com/tcalmant/jsonrpclib/issues) | |
365 | ||
366 | ### Additional headers | |
367 | ||
368 | If your remote service requires custom headers in request, you can pass them | |
369 | using the `headers` keyword argument, when creating the `ServerProxy`: | |
370 | ||
371 | ```python | |
372 | >>> import jsonrpclib | |
373 | >>> server = jsonrpclib.ServerProxy("http://localhost:8080", | |
374 | headers={'X-Test' : 'Test'}) | |
375 | ``` | |
376 | ||
377 | You can also put additional request headers only for certain method | |
378 | invocation: | |
379 | ||
380 | ```python | |
381 | >>> import jsonrpclib | |
382 | >>> server = jsonrpclib.Server("http://localhost:8080") | |
383 | >>> with server._additional_headers({'X-Test' : 'Test'}) as test_server: | |
384 | ... test_server.ping(42) | |
385 | ... | |
386 | >>> # X-Test header will be no longer sent in requests | |
387 | ``` | |
388 | ||
389 | Of course `_additional_headers` contexts can be nested as well. | |
390 | ||
391 | ## Class Translation | |
392 | ||
393 | The library supports an *"automatic"* class translation process, although it | |
394 | is turned off by default. | |
395 | This can be devastatingly slow if improperly used, so the following is just a | |
396 | short list of things to keep in mind when using it. | |
397 | ||
398 | - Keep It (the object) Simple Stupid. (for exceptions, keep reading) | |
399 | - Do not require init params (for exceptions, keep reading) | |
400 | - Getter properties without setters could be dangerous (read: not tested) | |
401 | ||
402 | If any of the above are issues, use the `_serialize` method (see usage below). | |
403 | The server and client must **BOTH** have the `use_jsonclass` configuration | |
404 | item on and they must both have access to the same libraries used by the | |
405 | objects for this to work. | |
406 | ||
407 | If you have excessively nested arguments, it would be better to turn off the | |
408 | translation and manually invoke it on specific objects using | |
409 | `jsonrpclib.jsonclass.dump` / `jsonrpclib.jsonclass.load` (since the | |
410 | default behavior recursively goes through attributes and lists/dicts/tuples). | |
411 | ||
412 | * Sample file: `test_obj.py` | |
413 | ||
414 | ```python | |
415 | # This object is /very/ simple, and the system will look through the | |
416 | # attributes and serialize what it can. | |
417 | class TestObj(object): | |
418 | foo = 'bar' | |
419 | ||
420 | # This object requires __init__ params, so it uses the _serialize method | |
421 | # and returns a tuple of init params and attribute values (the init params | |
422 | # can be a dict or a list, but the attribute values must be a dict.) | |
423 | class TestSerial(object): | |
424 | foo = 'bar' | |
425 | def __init__(self, *args): | |
426 | self.args = args | |
427 | def _serialize(self): | |
428 | return (self.args, {'foo':self.foo,}) | |
429 | ``` | |
430 | ||
431 | - Sample usage: | |
432 | ||
433 | ```python | |
434 | >>> import jsonrpclib | |
435 | >>> import test_obj | |
436 | ||
437 | # History is used only to print the serialized form of beans | |
438 | >>> history = jsonrpclib.history.History() | |
439 | >>> testobj1 = test_obj.TestObj() | |
440 | >>> testobj2 = test_obj.TestSerial() | |
441 | >>> server = jsonrpclib.Server('http://localhost:8080', history=history) | |
442 | ||
443 | # The 'ping' just returns whatever is sent | |
444 | >>> ping1 = server.ping(testobj1) | |
445 | >>> ping2 = server.ping(testobj2) | |
446 | ||
447 | >>> print(history.request) | |
448 | {"id": "7805f1f9-9abd-49c6-81dc-dbd47229fe13", "jsonrpc": "2.0", | |
449 | "method": "ping", "params": [{"__jsonclass__": | |
450 | ["test_obj.TestSerial", []], "foo": "bar"} | |
451 | ]} | |
452 | >>> print(history.response) | |
453 | {"id": "7805f1f9-9abd-49c6-81dc-dbd47229fe13", "jsonrpc": "2.0", | |
454 | "result": {"__jsonclass__": ["test_obj.TestSerial", []], "foo": "bar"}} | |
455 | ``` | |
456 | ||
457 | This behavior is turned on by default. | |
458 | To deactivate it, just set the `use_jsonclass` member of a server `Config` to | |
459 | `False`. | |
460 | If you want to use a per-class serialization method, set its name in the | |
461 | `serialize_method` member of a server `Config`. | |
462 | Finally, if you are using classes that you have defined in the implementation | |
463 | (as in, not a separate library), you'll need to add those | |
464 | (on **BOTH** the server and the client) using the `config.classes.add()` method. | |
465 | ||
466 | Feedback on this "feature" is very, VERY much appreciated. | |
467 | ||
468 | ## Tests | |
469 | ||
470 | Tests are an almost-verbatim drop from the JSON-RPC specification 2.0 | |
471 | page. They can be run using *unittest* or *nosetest*: | |
472 | ||
473 | ``` | |
474 | python -m unittest discover tests | |
475 | python3 -m unittest discover tests | |
476 | nosetests tests | |
477 | ``` | |
478 | ||
479 | ## Why JSON-RPC? | |
480 | ||
481 | In my opinion, there are several reasons to choose JSON over XML for RPC: | |
482 | ||
483 | * Much simpler to read (I suppose this is opinion, but I know I'm right. :) | |
484 | * Size / Bandwidth - Main reason, a JSON object representation is just much | |
485 | smaller. | |
486 | * Parsing - JSON should be much quicker to parse than XML. | |
487 | * Easy class passing with `jsonclass` (when enabled) | |
488 | ||
489 | In the interest of being fair, there are also a few reasons to choose XML over | |
490 | JSON: | |
491 | ||
492 | * Your server doesn't do JSON (rather obvious) | |
493 | * Wider XML-RPC support across APIs (can we change this? :)) | |
494 | * Libraries are more established, *i.e.* more stable (Let's change this too) | |
495 | ||
496 | Platform: UNKNOWN | |
497 | Classifier: Development Status :: 5 - Production/Stable | |
498 | Classifier: Intended Audience :: Developers | |
499 | Classifier: License :: OSI Approved :: Apache Software License | |
500 | Classifier: Operating System :: OS Independent | |
501 | Classifier: Programming Language :: Python :: 2.7 | |
502 | Classifier: Programming Language :: Python :: 3 | |
503 | Classifier: Programming Language :: Python :: 3.3 | |
504 | Classifier: Programming Language :: Python :: 3.4 | |
505 | Classifier: Programming Language :: Python :: 3.5 | |
506 | Classifier: Programming Language :: Python :: 3.6 | |
507 | Classifier: Programming Language :: Python :: 3.7 | |
508 | Description-Content-Type: text/markdown | |
0 | Metadata-Version: 2.1 | |
1 | Name: jsonrpclib-pelix | |
2 | Version: 0.4.2 | |
3 | Summary: This project is an implementation of the JSON-RPC v2.0 specification (backwards-compatible) as a client library, for Python 2.7 and Python 3. This version is a fork of jsonrpclib by Josh Marshall, made to be also usable with Pelix/iPOPO remote services. | |
4 | Home-page: http://github.com/tcalmant/jsonrpclib/ | |
5 | Author: Thomas Calmant | |
6 | Author-email: thomas.calmant+github@gmail.com | |
7 | License: Apache License 2.0 | |
8 | Description: # JSONRPClib (patched for Pelix and Python 3) | |
9 | ||
10 | [![Latest Version](https://img.shields.io/pypi/v/jsonrpclib-pelix.svg)](https://pypi.python.org/pypi/jsonrpclib-pelix/) | |
11 | [![License](https://img.shields.io/pypi/l/jsonrpclib-pelix.svg)](https://pypi.python.org/pypi/jsonrpclib-pelix/) | |
12 | [![Travis-CI status](https://travis-ci.org/tcalmant/jsonrpclib.svg?branch=master)](https://travis-ci.org/tcalmant/jsonrpclib) | |
13 | [![Coveralls status](https://coveralls.io/repos/tcalmant/jsonrpclib/badge.svg?branch=master)](https://coveralls.io/r/tcalmant/jsonrpclib?branch=master) | |
14 | ||
15 | This library is an implementation of the JSON-RPC specification. | |
16 | It supports both the original 1.0 specification, as well as the new | |
17 | (proposed) 2.0 specification, which includes batch submission, keyword | |
18 | arguments, etc. | |
19 | ||
20 | This library is licensed under the terms of the | |
21 | [Apache Software License 2.0](<http://www.apache.org/licenses/LICENSE-2.0.html>). | |
22 | ||
23 | ||
24 | ## About this version | |
25 | ||
26 | This is a patched version of the original `jsonrpclib` project by Josh Marshall, | |
27 | available at | |
28 | [joshmarshall/jsonrpclib](<https://github.com/joshmarshall/jsonrpclib>). | |
29 | ||
30 | The suffix *-pelix* only indicates that this version works with Pelix | |
31 | Remote Services, but it is **not** a Pelix specific implementation. | |
32 | ||
33 | * This version adds support for Python 3, staying compatible with Python 2.7. | |
34 | The support for Python 2.6 has been dropped, as it was becoming to hard to | |
35 | maintain. | |
36 | * It is now possible to use the `dispatch_method` argument while extending the | |
37 | `SimpleJSONRPCDispatcher`, to use a custom dispatcher. | |
38 | This allows to use this package by Pelix Remote Services. | |
39 | * It can use thread pools to control the number of threads spawned to handle | |
40 | notification requests and clients connections. | |
41 | * The modifications added in other forks of this project have been added: | |
42 | * From [drdaeman/jsonrpclib](<https://github.com/drdaeman/jsonrpclib>): | |
43 | * Improved JSON-RPC 1.0 support | |
44 | * Less strict error response handling | |
45 | * From [tuomassalo/jsonrpclib](<https://github.com/tuomassalo/jsonrpclib>): | |
46 | * In case of a non-predefined error, raise an AppError and give access | |
47 | to *error.data* | |
48 | * From [dejw/jsonrpclib](<https://github.com/dejw/jsonrpclib>): | |
49 | * Custom headers can be sent with request and associated tests | |
50 | * Since version 0.4, this package added back the support of Unix sockets. | |
51 | * This package cannot be installed with the original `jsonrpclib`, as it uses | |
52 | the same name. | |
53 | ||
54 | ## Summary | |
55 | ||
56 | This library implements the JSON-RPC 2.0 proposed specification in pure Python. | |
57 | It is designed to be as compatible with the syntax of `xmlrpclib` as possible | |
58 | (it extends where possible), so that projects using `xmlrpclib` could easily be | |
59 | modified to use JSON and experiment with the differences. | |
60 | ||
61 | It is backwards-compatible with the 1.0 specification, and supports all of the | |
62 | new proposed features of 2.0, including: | |
63 | ||
64 | - Batch submission (via the `MultiCall` class) | |
65 | - Keyword arguments | |
66 | - Notifications (both in a batch and 'normal') | |
67 | - Class translation using the `__jsonclass__` key. | |
68 | ||
69 | A `SimpleJSONRPCServer` class has been added. It is intended to emulate the | |
70 | `SimpleXMLRPCServer` from the default Python distribution. | |
71 | ||
72 | ## Requirements | |
73 | ||
74 | This library supports `cjson` and `simplejson`, and looks for the parsers in | |
75 | that order (searching first for `cjson`, then for the *built-in* `json` in 2.7+, | |
76 | and then the `simplejson` external library). | |
77 | One of these must be installed to use this library, although if you have a | |
78 | standard distribution of 2.7+, you should already have one. | |
79 | Keep in mind that `cjson` is supposed to be the quickest, I believe, so if you | |
80 | are going for full-on optimization you may want to pick it up. | |
81 | ||
82 | ## Installation | |
83 | ||
84 | You can install this from PyPI with one of the following commands (`sudo` | |
85 | might be required): | |
86 | ||
87 | ``` | |
88 | # Global installation | |
89 | pip install jsonrpclib-pelix | |
90 | ||
91 | # Local installation | |
92 | pip install --user jsonrpclib-pelix | |
93 | ``` | |
94 | ||
95 | Alternatively, you can download the source from the GitHub repository at | |
96 | [tcalmant/jsonrpclib](http://github.com/tcalmant/jsonrpclib) and manually | |
97 | install it with the following commands: | |
98 | ||
99 | ``` | |
100 | git clone git://github.com/tcalmant/jsonrpclib.git | |
101 | cd jsonrpclib | |
102 | python setup.py install | |
103 | ``` | |
104 | ||
105 | ## A note on logging | |
106 | ||
107 | `jsonrpclib-pelix` uses the `logging` module from the standard Python | |
108 | library to trace warnings and errors, but doesn't set it up. | |
109 | As a result, you have to configure the Python logging to print out traces. | |
110 | ||
111 | The easiest way to do it is to add those lines at the beginning of your code: | |
112 | ```python | |
113 | import logging | |
114 | logging.basiConfig() | |
115 | ``` | |
116 | ||
117 | More information can be found in the | |
118 | [`logging` documentation page](https://docs.python.org/3/library/logging.html). | |
119 | ||
120 | ## `SimpleJSONRPCServer` | |
121 | ||
122 | This is identical in usage (or should be) to the `SimpleXMLRPCServer` in the | |
123 | Python standard library. | |
124 | Some of the differences in features are that it obviously supports notification, | |
125 | batch calls, class translation (if left on), etc. | |
126 | ||
127 | **Note:** The import line is slightly different from the regular | |
128 | `SimpleXMLRPCServer`, since the `SimpleJSONRPCServer` is provided by th | |
129 | `jsonrpclib` library. | |
130 | ||
131 | ```python | |
132 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
133 | ||
134 | server = SimpleJSONRPCServer(('localhost', 8080)) | |
135 | server.register_function(pow) | |
136 | server.register_function(lambda x,y: x+y, 'add') | |
137 | server.register_function(lambda x: x, 'ping') | |
138 | server.serve_forever() | |
139 | ``` | |
140 | ||
141 | To start protect the server with SSL, use the following snippet: | |
142 | ||
143 | ```python | |
144 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
145 | import ssl | |
146 | ||
147 | # Setup the SSL socket | |
148 | server = SimpleJSONRPCServer(('localhost', 8080), bind_and_activate=False) | |
149 | server.socket = ssl.wrap_socket(server.socket, certfile='server.pem', | |
150 | server_side=True) | |
151 | server.server_bind() | |
152 | server.server_activate() | |
153 | ||
154 | # ... register functions | |
155 | # Start the server | |
156 | server.serve_forever() | |
157 | ``` | |
158 | ||
159 | ### Notification Thread Pool | |
160 | ||
161 | By default, notification calls are handled in the request handling thread. | |
162 | It is possible to use a thread pool to handle them, by giving it to the server | |
163 | using the `set_notification_pool()` method: | |
164 | ||
165 | ```python | |
166 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
167 | from jsonrpclib.threadpool import ThreadPool | |
168 | ||
169 | # Setup the thread pool: between 0 and 10 threads | |
170 | pool = ThreadPool(max_threads=10, min_threads=0) | |
171 | ||
172 | # Don't forget to start it | |
173 | pool.start() | |
174 | ||
175 | # Setup the server | |
176 | server = SimpleJSONRPCServer(('localhost', 8080)) | |
177 | server.set_notification_pool(pool) | |
178 | ||
179 | # Register methods | |
180 | server.register_function(pow) | |
181 | server.register_function(lambda x,y: x+y, 'add') | |
182 | server.register_function(lambda x: x, 'ping') | |
183 | ||
184 | try: | |
185 | server.serve_forever() | |
186 | finally: | |
187 | # Stop the thread pool (let threads finish their current task) | |
188 | pool.stop() | |
189 | server.set_notification_pool(None) | |
190 | ``` | |
191 | ||
192 | ### Threaded server | |
193 | ||
194 | It is also possible to use a thread pool to handle clients requests, using the | |
195 | `PooledJSONRPCServer` class. | |
196 | By default, this class uses pool of 0 to 30 threads. | |
197 | A custom pool can be given with the `thread_pool` parameter of the class | |
198 | constructor. | |
199 | ||
200 | The notification pool and the request pool are different: by default, a server | |
201 | with a request pool doesn't have a notification pool. | |
202 | ||
203 | ```python | |
204 | from jsonrpclib.SimpleJSONRPCServer import PooledJSONRPCServer | |
205 | from jsonrpclib.threadpool import ThreadPool | |
206 | ||
207 | # Setup the notification and request pools | |
208 | nofif_pool = ThreadPool(max_threads=10, min_threads=0) | |
209 | request_pool = ThreadPool(max_threads=50, min_threads=10) | |
210 | ||
211 | # Don't forget to start them | |
212 | nofif_pool.start() | |
213 | request_pool.start() | |
214 | ||
215 | # Setup the server | |
216 | server = PooledJSONRPCServer(('localhost', 8080), thread_pool=request_pool) | |
217 | server.set_notification_pool(nofif_pool) | |
218 | ||
219 | # Register methods | |
220 | server.register_function(pow) | |
221 | server.register_function(lambda x,y: x+y, 'add') | |
222 | server.register_function(lambda x: x, 'ping') | |
223 | ||
224 | try: | |
225 | server.serve_forever() | |
226 | finally: | |
227 | # Stop the thread pools (let threads finish their current task) | |
228 | request_pool.stop() | |
229 | nofif_pool.stop() | |
230 | server.set_notification_pool(None) | |
231 | ``` | |
232 | ||
233 | ### Unix socket | |
234 | ||
235 | To start a server listening on a Unix socket, you will have to use the | |
236 | following snippet: | |
237 | ||
238 | ```python | |
239 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
240 | import os | |
241 | import socket | |
242 | ||
243 | # Set the path to the socket file | |
244 | socket_name = "/tmp/my_socket.socket" | |
245 | ||
246 | # Ensure that the file doesn't exist yet (or an error will be raised) | |
247 | if os.path.exists(socket_name): | |
248 | os.remove(socket_name) | |
249 | ||
250 | try: | |
251 | # Start the server, indicating the socket family | |
252 | # The server will force some flags when in Unix socket mode | |
253 | # (no log request, no reuse address, ...) | |
254 | srv = SimpleJSONRPCServer(socket_name, address_family=socket.AF_UNIX) | |
255 | ||
256 | # ... register methods to the server | |
257 | # Run the server | |
258 | srv.serve_forever() | |
259 | except KeyboardInterrupt: | |
260 | # Shutdown the server gracefully | |
261 | srv.shutdown() | |
262 | srv.server_close() | |
263 | finally: | |
264 | # You should clean up after the server stopped | |
265 | os.remove(socket_name) | |
266 | ``` | |
267 | ||
268 | This feature is tested on Linux during Travis-CI builds. It also has | |
269 | been tested on Windows Subsystem for Linux (WSL) on Windows 10 1809. | |
270 | ||
271 | This feature is not available on "pure" Windows, as it doesn't provide | |
272 | the `AF_UNIX` address family. | |
273 | ||
274 | ## Client Usage | |
275 | ||
276 | This is (obviously) taken from a console session. | |
277 | ||
278 | ```python | |
279 | >>> import jsonrpclib | |
280 | >>> server = jsonrpclib.ServerProxy('http://localhost:8080') | |
281 | >>> server.add(5,6) | |
282 | 11 | |
283 | >>> server.add(x=5, y=10) | |
284 | 15 | |
285 | >>> server._notify.add(5,6) | |
286 | # No result returned... | |
287 | >>> batch = jsonrpclib.MultiCall(server) | |
288 | >>> batch.add(5, 6) | |
289 | >>> batch.ping({'key':'value'}) | |
290 | >>> batch._notify.add(4, 30) | |
291 | >>> results = batch() | |
292 | >>> for result in results: | |
293 | >>> ... print(result) | |
294 | 11 | |
295 | {'key': 'value'} | |
296 | # Note that there are only two responses -- this is according to spec. | |
297 | ||
298 | # Clean up | |
299 | >>> server('close')() | |
300 | ||
301 | # Using client history | |
302 | >>> history = jsonrpclib.history.History() | |
303 | >>> server = jsonrpclib.ServerProxy('http://localhost:8080', history=history) | |
304 | >>> server.add(5,6) | |
305 | 11 | |
306 | >>> print(history.request) | |
307 | {"id": "f682b956-c8e1-4506-9db4-29fe8bc9fcaa", "jsonrpc": "2.0", | |
308 | "method": "add", "params": [5, 6]} | |
309 | >>> print(history.response) | |
310 | {"id": "f682b956-c8e1-4506-9db4-29fe8bc9fcaa", "jsonrpc": "2.0", | |
311 | "result": 11} | |
312 | ||
313 | # Clean up | |
314 | >>> server('close')() | |
315 | ``` | |
316 | ||
317 | If you need 1.0 functionality, there are a bunch of places you can pass | |
318 | that in, although the best is just to give a specific configuration to | |
319 | `jsonrpclib.ServerProxy`: | |
320 | ||
321 | ```python | |
322 | >>> import jsonrpclib | |
323 | >>> jsonrpclib.config.DEFAULT.version | |
324 | 2.0 | |
325 | >>> config = jsonrpclib.config.Config(version=1.0) | |
326 | >>> history = jsonrpclib.history.History() | |
327 | >>> server = jsonrpclib.ServerProxy('http://localhost:8080', config=config, | |
328 | history=history) | |
329 | >>> server.add(7, 10) | |
330 | 17 | |
331 | >>> print(history.request) | |
332 | {"id": "827b2923-5b37-49a5-8b36-e73920a16d32", | |
333 | "method": "add", "params": [7, 10]} | |
334 | >>> print(history.response) | |
335 | {"id": "827b2923-5b37-49a5-8b36-e73920a16d32", "error": null, "result": 17} | |
336 | >>> server('close')() | |
337 | ``` | |
338 | ||
339 | The equivalent `loads` and `dumps` functions also exist, although with | |
340 | minor modifications. | |
341 | The `dumps` arguments are almost identical, but it adds three arguments: | |
342 | `rpcid` for the `id` key, `version` to specify the JSON-RPC compatibility, | |
343 | and `notify` if it's a request that you want to be a notification. | |
344 | ||
345 | Additionally, the `loads` method does not return the params and method like | |
346 | `xmlrpclib`, but instead | |
347 | a.) parses for errors, raising ProtocolErrors, and | |
348 | b.) returns the entire structure of the request / response for manual parsing. | |
349 | ||
350 | ### Unix sockets | |
351 | ||
352 | To connect a JSON-RPC server over a Unix socket, you have to use a specific | |
353 | protocol: `unix+http`. | |
354 | ||
355 | When connecting to a Unix socket in the current working directory, you can use | |
356 | the following syntax: `unix+http://my.socket` | |
357 | ||
358 | When you need to give an absolute path you must use the path part of the URL, | |
359 | the host part will be ignored. For example, you can use this URL to indicate a | |
360 | Unix socket in `/var/lib/daemon.socket`: `unix+http://./var/lib/daemon.socket` | |
361 | ||
362 | **Note:** Currently, only HTTP is supported over a Unix socket. | |
363 | If you want HTTPS support to be implemented, please create an | |
364 | [issue on GitHub](https://github.com/tcalmant/jsonrpclib/issues) | |
365 | ||
366 | ### Additional headers | |
367 | ||
368 | If your remote service requires custom headers in request, you can pass them | |
369 | using the `headers` keyword argument, when creating the `ServerProxy`: | |
370 | ||
371 | ```python | |
372 | >>> import jsonrpclib | |
373 | >>> server = jsonrpclib.ServerProxy("http://localhost:8080", | |
374 | headers={'X-Test' : 'Test'}) | |
375 | ``` | |
376 | ||
377 | You can also put additional request headers only for certain method | |
378 | invocation: | |
379 | ||
380 | ```python | |
381 | >>> import jsonrpclib | |
382 | >>> server = jsonrpclib.Server("http://localhost:8080") | |
383 | >>> with server._additional_headers({'X-Test' : 'Test'}) as test_server: | |
384 | ... test_server.ping(42) | |
385 | ... | |
386 | >>> # X-Test header will be no longer sent in requests | |
387 | ``` | |
388 | ||
389 | Of course `_additional_headers` contexts can be nested as well. | |
390 | ||
391 | ## Class Translation | |
392 | ||
393 | The library supports an *"automatic"* class translation process, although it | |
394 | is turned off by default. | |
395 | This can be devastatingly slow if improperly used, so the following is just a | |
396 | short list of things to keep in mind when using it. | |
397 | ||
398 | - Keep It (the object) Simple Stupid. (for exceptions, keep reading) | |
399 | - Do not require init params (for exceptions, keep reading) | |
400 | - Getter properties without setters could be dangerous (read: not tested) | |
401 | ||
402 | If any of the above are issues, use the `_serialize` method (see usage below). | |
403 | The server and client must **BOTH** have the `use_jsonclass` configuration | |
404 | item on and they must both have access to the same libraries used by the | |
405 | objects for this to work. | |
406 | ||
407 | If you have excessively nested arguments, it would be better to turn off the | |
408 | translation and manually invoke it on specific objects using | |
409 | `jsonrpclib.jsonclass.dump` / `jsonrpclib.jsonclass.load` (since the | |
410 | default behavior recursively goes through attributes and lists/dicts/tuples). | |
411 | ||
412 | * Sample file: `test_obj.py` | |
413 | ||
414 | ```python | |
415 | # This object is /very/ simple, and the system will look through the | |
416 | # attributes and serialize what it can. | |
417 | class TestObj(object): | |
418 | foo = 'bar' | |
419 | ||
420 | # This object requires __init__ params, so it uses the _serialize method | |
421 | # and returns a tuple of init params and attribute values (the init params | |
422 | # can be a dict or a list, but the attribute values must be a dict.) | |
423 | class TestSerial(object): | |
424 | foo = 'bar' | |
425 | def __init__(self, *args): | |
426 | self.args = args | |
427 | def _serialize(self): | |
428 | return (self.args, {'foo':self.foo,}) | |
429 | ``` | |
430 | ||
431 | - Sample usage: | |
432 | ||
433 | ```python | |
434 | >>> import jsonrpclib | |
435 | >>> import test_obj | |
436 | ||
437 | # History is used only to print the serialized form of beans | |
438 | >>> history = jsonrpclib.history.History() | |
439 | >>> testobj1 = test_obj.TestObj() | |
440 | >>> testobj2 = test_obj.TestSerial() | |
441 | >>> server = jsonrpclib.Server('http://localhost:8080', history=history) | |
442 | ||
443 | # The 'ping' just returns whatever is sent | |
444 | >>> ping1 = server.ping(testobj1) | |
445 | >>> ping2 = server.ping(testobj2) | |
446 | ||
447 | >>> print(history.request) | |
448 | {"id": "7805f1f9-9abd-49c6-81dc-dbd47229fe13", "jsonrpc": "2.0", | |
449 | "method": "ping", "params": [{"__jsonclass__": | |
450 | ["test_obj.TestSerial", []], "foo": "bar"} | |
451 | ]} | |
452 | >>> print(history.response) | |
453 | {"id": "7805f1f9-9abd-49c6-81dc-dbd47229fe13", "jsonrpc": "2.0", | |
454 | "result": {"__jsonclass__": ["test_obj.TestSerial", []], "foo": "bar"}} | |
455 | ``` | |
456 | ||
457 | This behavior is turned on by default. | |
458 | To deactivate it, just set the `use_jsonclass` member of a server `Config` to | |
459 | `False`. | |
460 | If you want to use a per-class serialization method, set its name in the | |
461 | `serialize_method` member of a server `Config`. | |
462 | Finally, if you are using classes that you have defined in the implementation | |
463 | (as in, not a separate library), you'll need to add those | |
464 | (on **BOTH** the server and the client) using the `config.classes.add()` method. | |
465 | ||
466 | Feedback on this "feature" is very, VERY much appreciated. | |
467 | ||
468 | ## Tests | |
469 | ||
470 | Tests are an almost-verbatim drop from the JSON-RPC specification 2.0 | |
471 | page. They can be run using *unittest* or *nosetest*: | |
472 | ||
473 | ``` | |
474 | python -m unittest discover tests | |
475 | python3 -m unittest discover tests | |
476 | nosetests tests | |
477 | ``` | |
478 | ||
479 | ## Why JSON-RPC? | |
480 | ||
481 | In my opinion, there are several reasons to choose JSON over XML for RPC: | |
482 | ||
483 | * Much simpler to read (I suppose this is opinion, but I know I'm right. :) | |
484 | * Size / Bandwidth - Main reason, a JSON object representation is just much | |
485 | smaller. | |
486 | * Parsing - JSON should be much quicker to parse than XML. | |
487 | * Easy class passing with `jsonclass` (when enabled) | |
488 | ||
489 | In the interest of being fair, there are also a few reasons to choose XML over | |
490 | JSON: | |
491 | ||
492 | * Your server doesn't do JSON (rather obvious) | |
493 | * Wider XML-RPC support across APIs (can we change this? :)) | |
494 | * Libraries are more established, *i.e.* more stable (Let's change this too) | |
495 | ||
496 | Platform: UNKNOWN | |
497 | Classifier: Development Status :: 5 - Production/Stable | |
498 | Classifier: Intended Audience :: Developers | |
499 | Classifier: License :: OSI Approved :: Apache Software License | |
500 | Classifier: Operating System :: OS Independent | |
501 | Classifier: Programming Language :: Python :: 2.7 | |
502 | Classifier: Programming Language :: Python :: 3 | |
503 | Classifier: Programming Language :: Python :: 3.3 | |
504 | Classifier: Programming Language :: Python :: 3.4 | |
505 | Classifier: Programming Language :: Python :: 3.5 | |
506 | Classifier: Programming Language :: Python :: 3.6 | |
507 | Classifier: Programming Language :: Python :: 3.7 | |
508 | Description-Content-Type: text/markdown |
0 | Metadata-Version: 2.1 | |
1 | Name: jsonrpclib-pelix | |
2 | Version: 0.4.2 | |
3 | Summary: This project is an implementation of the JSON-RPC v2.0 specification (backwards-compatible) as a client library, for Python 2.7 and Python 3. This version is a fork of jsonrpclib by Josh Marshall, made to be also usable with Pelix/iPOPO remote services. | |
4 | Home-page: http://github.com/tcalmant/jsonrpclib/ | |
5 | Author: Thomas Calmant | |
6 | Author-email: thomas.calmant+github@gmail.com | |
7 | License: Apache License 2.0 | |
8 | Description: # JSONRPClib (patched for Pelix and Python 3) | |
9 | ||
10 | [![Latest Version](https://img.shields.io/pypi/v/jsonrpclib-pelix.svg)](https://pypi.python.org/pypi/jsonrpclib-pelix/) | |
11 | [![License](https://img.shields.io/pypi/l/jsonrpclib-pelix.svg)](https://pypi.python.org/pypi/jsonrpclib-pelix/) | |
12 | [![Travis-CI status](https://travis-ci.org/tcalmant/jsonrpclib.svg?branch=master)](https://travis-ci.org/tcalmant/jsonrpclib) | |
13 | [![Coveralls status](https://coveralls.io/repos/tcalmant/jsonrpclib/badge.svg?branch=master)](https://coveralls.io/r/tcalmant/jsonrpclib?branch=master) | |
14 | ||
15 | This library is an implementation of the JSON-RPC specification. | |
16 | It supports both the original 1.0 specification, as well as the new | |
17 | (proposed) 2.0 specification, which includes batch submission, keyword | |
18 | arguments, etc. | |
19 | ||
20 | This library is licensed under the terms of the | |
21 | [Apache Software License 2.0](<http://www.apache.org/licenses/LICENSE-2.0.html>). | |
22 | ||
23 | ||
24 | ## About this version | |
25 | ||
26 | This is a patched version of the original `jsonrpclib` project by Josh Marshall, | |
27 | available at | |
28 | [joshmarshall/jsonrpclib](<https://github.com/joshmarshall/jsonrpclib>). | |
29 | ||
30 | The suffix *-pelix* only indicates that this version works with Pelix | |
31 | Remote Services, but it is **not** a Pelix specific implementation. | |
32 | ||
33 | * This version adds support for Python 3, staying compatible with Python 2.7. | |
34 | The support for Python 2.6 has been dropped, as it was becoming to hard to | |
35 | maintain. | |
36 | * It is now possible to use the `dispatch_method` argument while extending the | |
37 | `SimpleJSONRPCDispatcher`, to use a custom dispatcher. | |
38 | This allows to use this package by Pelix Remote Services. | |
39 | * It can use thread pools to control the number of threads spawned to handle | |
40 | notification requests and clients connections. | |
41 | * The modifications added in other forks of this project have been added: | |
42 | * From [drdaeman/jsonrpclib](<https://github.com/drdaeman/jsonrpclib>): | |
43 | * Improved JSON-RPC 1.0 support | |
44 | * Less strict error response handling | |
45 | * From [tuomassalo/jsonrpclib](<https://github.com/tuomassalo/jsonrpclib>): | |
46 | * In case of a non-predefined error, raise an AppError and give access | |
47 | to *error.data* | |
48 | * From [dejw/jsonrpclib](<https://github.com/dejw/jsonrpclib>): | |
49 | * Custom headers can be sent with request and associated tests | |
50 | * Since version 0.4, this package added back the support of Unix sockets. | |
51 | * This package cannot be installed with the original `jsonrpclib`, as it uses | |
52 | the same name. | |
53 | ||
54 | ## Summary | |
55 | ||
56 | This library implements the JSON-RPC 2.0 proposed specification in pure Python. | |
57 | It is designed to be as compatible with the syntax of `xmlrpclib` as possible | |
58 | (it extends where possible), so that projects using `xmlrpclib` could easily be | |
59 | modified to use JSON and experiment with the differences. | |
60 | ||
61 | It is backwards-compatible with the 1.0 specification, and supports all of the | |
62 | new proposed features of 2.0, including: | |
63 | ||
64 | - Batch submission (via the `MultiCall` class) | |
65 | - Keyword arguments | |
66 | - Notifications (both in a batch and 'normal') | |
67 | - Class translation using the `__jsonclass__` key. | |
68 | ||
69 | A `SimpleJSONRPCServer` class has been added. It is intended to emulate the | |
70 | `SimpleXMLRPCServer` from the default Python distribution. | |
71 | ||
72 | ## Requirements | |
73 | ||
74 | This library supports `cjson` and `simplejson`, and looks for the parsers in | |
75 | that order (searching first for `cjson`, then for the *built-in* `json` in 2.7+, | |
76 | and then the `simplejson` external library). | |
77 | One of these must be installed to use this library, although if you have a | |
78 | standard distribution of 2.7+, you should already have one. | |
79 | Keep in mind that `cjson` is supposed to be the quickest, I believe, so if you | |
80 | are going for full-on optimization you may want to pick it up. | |
81 | ||
82 | ## Installation | |
83 | ||
84 | You can install this from PyPI with one of the following commands (`sudo` | |
85 | might be required): | |
86 | ||
87 | ``` | |
88 | # Global installation | |
89 | pip install jsonrpclib-pelix | |
90 | ||
91 | # Local installation | |
92 | pip install --user jsonrpclib-pelix | |
93 | ``` | |
94 | ||
95 | Alternatively, you can download the source from the GitHub repository at | |
96 | [tcalmant/jsonrpclib](http://github.com/tcalmant/jsonrpclib) and manually | |
97 | install it with the following commands: | |
98 | ||
99 | ``` | |
100 | git clone git://github.com/tcalmant/jsonrpclib.git | |
101 | cd jsonrpclib | |
102 | python setup.py install | |
103 | ``` | |
104 | ||
105 | ## A note on logging | |
106 | ||
107 | `jsonrpclib-pelix` uses the `logging` module from the standard Python | |
108 | library to trace warnings and errors, but doesn't set it up. | |
109 | As a result, you have to configure the Python logging to print out traces. | |
110 | ||
111 | The easiest way to do it is to add those lines at the beginning of your code: | |
112 | ```python | |
113 | import logging | |
114 | logging.basiConfig() | |
115 | ``` | |
116 | ||
117 | More information can be found in the | |
118 | [`logging` documentation page](https://docs.python.org/3/library/logging.html). | |
119 | ||
120 | ## `SimpleJSONRPCServer` | |
121 | ||
122 | This is identical in usage (or should be) to the `SimpleXMLRPCServer` in the | |
123 | Python standard library. | |
124 | Some of the differences in features are that it obviously supports notification, | |
125 | batch calls, class translation (if left on), etc. | |
126 | ||
127 | **Note:** The import line is slightly different from the regular | |
128 | `SimpleXMLRPCServer`, since the `SimpleJSONRPCServer` is provided by th | |
129 | `jsonrpclib` library. | |
130 | ||
131 | ```python | |
132 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
133 | ||
134 | server = SimpleJSONRPCServer(('localhost', 8080)) | |
135 | server.register_function(pow) | |
136 | server.register_function(lambda x,y: x+y, 'add') | |
137 | server.register_function(lambda x: x, 'ping') | |
138 | server.serve_forever() | |
139 | ``` | |
140 | ||
141 | To start protect the server with SSL, use the following snippet: | |
142 | ||
143 | ```python | |
144 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
145 | import ssl | |
146 | ||
147 | # Setup the SSL socket | |
148 | server = SimpleJSONRPCServer(('localhost', 8080), bind_and_activate=False) | |
149 | server.socket = ssl.wrap_socket(server.socket, certfile='server.pem', | |
150 | server_side=True) | |
151 | server.server_bind() | |
152 | server.server_activate() | |
153 | ||
154 | # ... register functions | |
155 | # Start the server | |
156 | server.serve_forever() | |
157 | ``` | |
158 | ||
159 | ### Notification Thread Pool | |
160 | ||
161 | By default, notification calls are handled in the request handling thread. | |
162 | It is possible to use a thread pool to handle them, by giving it to the server | |
163 | using the `set_notification_pool()` method: | |
164 | ||
165 | ```python | |
166 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
167 | from jsonrpclib.threadpool import ThreadPool | |
168 | ||
169 | # Setup the thread pool: between 0 and 10 threads | |
170 | pool = ThreadPool(max_threads=10, min_threads=0) | |
171 | ||
172 | # Don't forget to start it | |
173 | pool.start() | |
174 | ||
175 | # Setup the server | |
176 | server = SimpleJSONRPCServer(('localhost', 8080)) | |
177 | server.set_notification_pool(pool) | |
178 | ||
179 | # Register methods | |
180 | server.register_function(pow) | |
181 | server.register_function(lambda x,y: x+y, 'add') | |
182 | server.register_function(lambda x: x, 'ping') | |
183 | ||
184 | try: | |
185 | server.serve_forever() | |
186 | finally: | |
187 | # Stop the thread pool (let threads finish their current task) | |
188 | pool.stop() | |
189 | server.set_notification_pool(None) | |
190 | ``` | |
191 | ||
192 | ### Threaded server | |
193 | ||
194 | It is also possible to use a thread pool to handle clients requests, using the | |
195 | `PooledJSONRPCServer` class. | |
196 | By default, this class uses pool of 0 to 30 threads. | |
197 | A custom pool can be given with the `thread_pool` parameter of the class | |
198 | constructor. | |
199 | ||
200 | The notification pool and the request pool are different: by default, a server | |
201 | with a request pool doesn't have a notification pool. | |
202 | ||
203 | ```python | |
204 | from jsonrpclib.SimpleJSONRPCServer import PooledJSONRPCServer | |
205 | from jsonrpclib.threadpool import ThreadPool | |
206 | ||
207 | # Setup the notification and request pools | |
208 | nofif_pool = ThreadPool(max_threads=10, min_threads=0) | |
209 | request_pool = ThreadPool(max_threads=50, min_threads=10) | |
210 | ||
211 | # Don't forget to start them | |
212 | nofif_pool.start() | |
213 | request_pool.start() | |
214 | ||
215 | # Setup the server | |
216 | server = PooledJSONRPCServer(('localhost', 8080), thread_pool=request_pool) | |
217 | server.set_notification_pool(nofif_pool) | |
218 | ||
219 | # Register methods | |
220 | server.register_function(pow) | |
221 | server.register_function(lambda x,y: x+y, 'add') | |
222 | server.register_function(lambda x: x, 'ping') | |
223 | ||
224 | try: | |
225 | server.serve_forever() | |
226 | finally: | |
227 | # Stop the thread pools (let threads finish their current task) | |
228 | request_pool.stop() | |
229 | nofif_pool.stop() | |
230 | server.set_notification_pool(None) | |
231 | ``` | |
232 | ||
233 | ### Unix socket | |
234 | ||
235 | To start a server listening on a Unix socket, you will have to use the | |
236 | following snippet: | |
237 | ||
238 | ```python | |
239 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
240 | import os | |
241 | import socket | |
242 | ||
243 | # Set the path to the socket file | |
244 | socket_name = "/tmp/my_socket.socket" | |
245 | ||
246 | # Ensure that the file doesn't exist yet (or an error will be raised) | |
247 | if os.path.exists(socket_name): | |
248 | os.remove(socket_name) | |
249 | ||
250 | try: | |
251 | # Start the server, indicating the socket family | |
252 | # The server will force some flags when in Unix socket mode | |
253 | # (no log request, no reuse address, ...) | |
254 | srv = SimpleJSONRPCServer(socket_name, address_family=socket.AF_UNIX) | |
255 | ||
256 | # ... register methods to the server | |
257 | # Run the server | |
258 | srv.serve_forever() | |
259 | except KeyboardInterrupt: | |
260 | # Shutdown the server gracefully | |
261 | srv.shutdown() | |
262 | srv.server_close() | |
263 | finally: | |
264 | # You should clean up after the server stopped | |
265 | os.remove(socket_name) | |
266 | ``` | |
267 | ||
268 | This feature is tested on Linux during Travis-CI builds. It also has | |
269 | been tested on Windows Subsystem for Linux (WSL) on Windows 10 1809. | |
270 | ||
271 | This feature is not available on "pure" Windows, as it doesn't provide | |
272 | the `AF_UNIX` address family. | |
273 | ||
274 | ## Client Usage | |
275 | ||
276 | This is (obviously) taken from a console session. | |
277 | ||
278 | ```python | |
279 | >>> import jsonrpclib | |
280 | >>> server = jsonrpclib.ServerProxy('http://localhost:8080') | |
281 | >>> server.add(5,6) | |
282 | 11 | |
283 | >>> server.add(x=5, y=10) | |
284 | 15 | |
285 | >>> server._notify.add(5,6) | |
286 | # No result returned... | |
287 | >>> batch = jsonrpclib.MultiCall(server) | |
288 | >>> batch.add(5, 6) | |
289 | >>> batch.ping({'key':'value'}) | |
290 | >>> batch._notify.add(4, 30) | |
291 | >>> results = batch() | |
292 | >>> for result in results: | |
293 | >>> ... print(result) | |
294 | 11 | |
295 | {'key': 'value'} | |
296 | # Note that there are only two responses -- this is according to spec. | |
297 | ||
298 | # Clean up | |
299 | >>> server('close')() | |
300 | ||
301 | # Using client history | |
302 | >>> history = jsonrpclib.history.History() | |
303 | >>> server = jsonrpclib.ServerProxy('http://localhost:8080', history=history) | |
304 | >>> server.add(5,6) | |
305 | 11 | |
306 | >>> print(history.request) | |
307 | {"id": "f682b956-c8e1-4506-9db4-29fe8bc9fcaa", "jsonrpc": "2.0", | |
308 | "method": "add", "params": [5, 6]} | |
309 | >>> print(history.response) | |
310 | {"id": "f682b956-c8e1-4506-9db4-29fe8bc9fcaa", "jsonrpc": "2.0", | |
311 | "result": 11} | |
312 | ||
313 | # Clean up | |
314 | >>> server('close')() | |
315 | ``` | |
316 | ||
317 | If you need 1.0 functionality, there are a bunch of places you can pass | |
318 | that in, although the best is just to give a specific configuration to | |
319 | `jsonrpclib.ServerProxy`: | |
320 | ||
321 | ```python | |
322 | >>> import jsonrpclib | |
323 | >>> jsonrpclib.config.DEFAULT.version | |
324 | 2.0 | |
325 | >>> config = jsonrpclib.config.Config(version=1.0) | |
326 | >>> history = jsonrpclib.history.History() | |
327 | >>> server = jsonrpclib.ServerProxy('http://localhost:8080', config=config, | |
328 | history=history) | |
329 | >>> server.add(7, 10) | |
330 | 17 | |
331 | >>> print(history.request) | |
332 | {"id": "827b2923-5b37-49a5-8b36-e73920a16d32", | |
333 | "method": "add", "params": [7, 10]} | |
334 | >>> print(history.response) | |
335 | {"id": "827b2923-5b37-49a5-8b36-e73920a16d32", "error": null, "result": 17} | |
336 | >>> server('close')() | |
337 | ``` | |
338 | ||
339 | The equivalent `loads` and `dumps` functions also exist, although with | |
340 | minor modifications. | |
341 | The `dumps` arguments are almost identical, but it adds three arguments: | |
342 | `rpcid` for the `id` key, `version` to specify the JSON-RPC compatibility, | |
343 | and `notify` if it's a request that you want to be a notification. | |
344 | ||
345 | Additionally, the `loads` method does not return the params and method like | |
346 | `xmlrpclib`, but instead | |
347 | a.) parses for errors, raising ProtocolErrors, and | |
348 | b.) returns the entire structure of the request / response for manual parsing. | |
349 | ||
350 | ### Unix sockets | |
351 | ||
352 | To connect a JSON-RPC server over a Unix socket, you have to use a specific | |
353 | protocol: `unix+http`. | |
354 | ||
355 | When connecting to a Unix socket in the current working directory, you can use | |
356 | the following syntax: `unix+http://my.socket` | |
357 | ||
358 | When you need to give an absolute path you must use the path part of the URL, | |
359 | the host part will be ignored. For example, you can use this URL to indicate a | |
360 | Unix socket in `/var/lib/daemon.socket`: `unix+http://./var/lib/daemon.socket` | |
361 | ||
362 | **Note:** Currently, only HTTP is supported over a Unix socket. | |
363 | If you want HTTPS support to be implemented, please create an | |
364 | [issue on GitHub](https://github.com/tcalmant/jsonrpclib/issues) | |
365 | ||
366 | ### Additional headers | |
367 | ||
368 | If your remote service requires custom headers in request, you can pass them | |
369 | using the `headers` keyword argument, when creating the `ServerProxy`: | |
370 | ||
371 | ```python | |
372 | >>> import jsonrpclib | |
373 | >>> server = jsonrpclib.ServerProxy("http://localhost:8080", | |
374 | headers={'X-Test' : 'Test'}) | |
375 | ``` | |
376 | ||
377 | You can also put additional request headers only for certain method | |
378 | invocation: | |
379 | ||
380 | ```python | |
381 | >>> import jsonrpclib | |
382 | >>> server = jsonrpclib.Server("http://localhost:8080") | |
383 | >>> with server._additional_headers({'X-Test' : 'Test'}) as test_server: | |
384 | ... test_server.ping(42) | |
385 | ... | |
386 | >>> # X-Test header will be no longer sent in requests | |
387 | ``` | |
388 | ||
389 | Of course `_additional_headers` contexts can be nested as well. | |
390 | ||
391 | ## Class Translation | |
392 | ||
393 | The library supports an *"automatic"* class translation process, although it | |
394 | is turned off by default. | |
395 | This can be devastatingly slow if improperly used, so the following is just a | |
396 | short list of things to keep in mind when using it. | |
397 | ||
398 | - Keep It (the object) Simple Stupid. (for exceptions, keep reading) | |
399 | - Do not require init params (for exceptions, keep reading) | |
400 | - Getter properties without setters could be dangerous (read: not tested) | |
401 | ||
402 | If any of the above are issues, use the `_serialize` method (see usage below). | |
403 | The server and client must **BOTH** have the `use_jsonclass` configuration | |
404 | item on and they must both have access to the same libraries used by the | |
405 | objects for this to work. | |
406 | ||
407 | If you have excessively nested arguments, it would be better to turn off the | |
408 | translation and manually invoke it on specific objects using | |
409 | `jsonrpclib.jsonclass.dump` / `jsonrpclib.jsonclass.load` (since the | |
410 | default behavior recursively goes through attributes and lists/dicts/tuples). | |
411 | ||
412 | * Sample file: `test_obj.py` | |
413 | ||
414 | ```python | |
415 | # This object is /very/ simple, and the system will look through the | |
416 | # attributes and serialize what it can. | |
417 | class TestObj(object): | |
418 | foo = 'bar' | |
419 | ||
420 | # This object requires __init__ params, so it uses the _serialize method | |
421 | # and returns a tuple of init params and attribute values (the init params | |
422 | # can be a dict or a list, but the attribute values must be a dict.) | |
423 | class TestSerial(object): | |
424 | foo = 'bar' | |
425 | def __init__(self, *args): | |
426 | self.args = args | |
427 | def _serialize(self): | |
428 | return (self.args, {'foo':self.foo,}) | |
429 | ``` | |
430 | ||
431 | - Sample usage: | |
432 | ||
433 | ```python | |
434 | >>> import jsonrpclib | |
435 | >>> import test_obj | |
436 | ||
437 | # History is used only to print the serialized form of beans | |
438 | >>> history = jsonrpclib.history.History() | |
439 | >>> testobj1 = test_obj.TestObj() | |
440 | >>> testobj2 = test_obj.TestSerial() | |
441 | >>> server = jsonrpclib.Server('http://localhost:8080', history=history) | |
442 | ||
443 | # The 'ping' just returns whatever is sent | |
444 | >>> ping1 = server.ping(testobj1) | |
445 | >>> ping2 = server.ping(testobj2) | |
446 | ||
447 | >>> print(history.request) | |
448 | {"id": "7805f1f9-9abd-49c6-81dc-dbd47229fe13", "jsonrpc": "2.0", | |
449 | "method": "ping", "params": [{"__jsonclass__": | |
450 | ["test_obj.TestSerial", []], "foo": "bar"} | |
451 | ]} | |
452 | >>> print(history.response) | |
453 | {"id": "7805f1f9-9abd-49c6-81dc-dbd47229fe13", "jsonrpc": "2.0", | |
454 | "result": {"__jsonclass__": ["test_obj.TestSerial", []], "foo": "bar"}} | |
455 | ``` | |
456 | ||
457 | This behavior is turned on by default. | |
458 | To deactivate it, just set the `use_jsonclass` member of a server `Config` to | |
459 | `False`. | |
460 | If you want to use a per-class serialization method, set its name in the | |
461 | `serialize_method` member of a server `Config`. | |
462 | Finally, if you are using classes that you have defined in the implementation | |
463 | (as in, not a separate library), you'll need to add those | |
464 | (on **BOTH** the server and the client) using the `config.classes.add()` method. | |
465 | ||
466 | Feedback on this "feature" is very, VERY much appreciated. | |
467 | ||
468 | ## Tests | |
469 | ||
470 | Tests are an almost-verbatim drop from the JSON-RPC specification 2.0 | |
471 | page. They can be run using *unittest* or *nosetest*: | |
472 | ||
473 | ``` | |
474 | python -m unittest discover tests | |
475 | python3 -m unittest discover tests | |
476 | nosetests tests | |
477 | ``` | |
478 | ||
479 | ## Why JSON-RPC? | |
480 | ||
481 | In my opinion, there are several reasons to choose JSON over XML for RPC: | |
482 | ||
483 | * Much simpler to read (I suppose this is opinion, but I know I'm right. :) | |
484 | * Size / Bandwidth - Main reason, a JSON object representation is just much | |
485 | smaller. | |
486 | * Parsing - JSON should be much quicker to parse than XML. | |
487 | * Easy class passing with `jsonclass` (when enabled) | |
488 | ||
489 | In the interest of being fair, there are also a few reasons to choose XML over | |
490 | JSON: | |
491 | ||
492 | * Your server doesn't do JSON (rather obvious) | |
493 | * Wider XML-RPC support across APIs (can we change this? :)) | |
494 | * Libraries are more established, *i.e.* more stable (Let's change this too) | |
495 | ||
496 | Platform: UNKNOWN | |
497 | Classifier: Development Status :: 5 - Production/Stable | |
498 | Classifier: Intended Audience :: Developers | |
499 | Classifier: License :: OSI Approved :: Apache Software License | |
500 | Classifier: Operating System :: OS Independent | |
501 | Classifier: Programming Language :: Python :: 2.7 | |
502 | Classifier: Programming Language :: Python :: 3 | |
503 | Classifier: Programming Language :: Python :: 3.3 | |
504 | Classifier: Programming Language :: Python :: 3.4 | |
505 | Classifier: Programming Language :: Python :: 3.5 | |
506 | Classifier: Programming Language :: Python :: 3.6 | |
507 | Classifier: Programming Language :: Python :: 3.7 | |
508 | Description-Content-Type: text/markdown | |
0 | Metadata-Version: 2.1 | |
1 | Name: jsonrpclib-pelix | |
2 | Version: 0.4.2 | |
3 | Summary: This project is an implementation of the JSON-RPC v2.0 specification (backwards-compatible) as a client library, for Python 2.7 and Python 3. This version is a fork of jsonrpclib by Josh Marshall, made to be also usable with Pelix/iPOPO remote services. | |
4 | Home-page: http://github.com/tcalmant/jsonrpclib/ | |
5 | Author: Thomas Calmant | |
6 | Author-email: thomas.calmant+github@gmail.com | |
7 | License: Apache License 2.0 | |
8 | Description: # JSONRPClib (patched for Pelix and Python 3) | |
9 | ||
10 | [![Latest Version](https://img.shields.io/pypi/v/jsonrpclib-pelix.svg)](https://pypi.python.org/pypi/jsonrpclib-pelix/) | |
11 | [![License](https://img.shields.io/pypi/l/jsonrpclib-pelix.svg)](https://pypi.python.org/pypi/jsonrpclib-pelix/) | |
12 | [![Travis-CI status](https://travis-ci.org/tcalmant/jsonrpclib.svg?branch=master)](https://travis-ci.org/tcalmant/jsonrpclib) | |
13 | [![Coveralls status](https://coveralls.io/repos/tcalmant/jsonrpclib/badge.svg?branch=master)](https://coveralls.io/r/tcalmant/jsonrpclib?branch=master) | |
14 | ||
15 | This library is an implementation of the JSON-RPC specification. | |
16 | It supports both the original 1.0 specification, as well as the new | |
17 | (proposed) 2.0 specification, which includes batch submission, keyword | |
18 | arguments, etc. | |
19 | ||
20 | This library is licensed under the terms of the | |
21 | [Apache Software License 2.0](<http://www.apache.org/licenses/LICENSE-2.0.html>). | |
22 | ||
23 | ||
24 | ## About this version | |
25 | ||
26 | This is a patched version of the original `jsonrpclib` project by Josh Marshall, | |
27 | available at | |
28 | [joshmarshall/jsonrpclib](<https://github.com/joshmarshall/jsonrpclib>). | |
29 | ||
30 | The suffix *-pelix* only indicates that this version works with Pelix | |
31 | Remote Services, but it is **not** a Pelix specific implementation. | |
32 | ||
33 | * This version adds support for Python 3, staying compatible with Python 2.7. | |
34 | The support for Python 2.6 has been dropped, as it was becoming to hard to | |
35 | maintain. | |
36 | * It is now possible to use the `dispatch_method` argument while extending the | |
37 | `SimpleJSONRPCDispatcher`, to use a custom dispatcher. | |
38 | This allows to use this package by Pelix Remote Services. | |
39 | * It can use thread pools to control the number of threads spawned to handle | |
40 | notification requests and clients connections. | |
41 | * The modifications added in other forks of this project have been added: | |
42 | * From [drdaeman/jsonrpclib](<https://github.com/drdaeman/jsonrpclib>): | |
43 | * Improved JSON-RPC 1.0 support | |
44 | * Less strict error response handling | |
45 | * From [tuomassalo/jsonrpclib](<https://github.com/tuomassalo/jsonrpclib>): | |
46 | * In case of a non-predefined error, raise an AppError and give access | |
47 | to *error.data* | |
48 | * From [dejw/jsonrpclib](<https://github.com/dejw/jsonrpclib>): | |
49 | * Custom headers can be sent with request and associated tests | |
50 | * Since version 0.4, this package added back the support of Unix sockets. | |
51 | * This package cannot be installed with the original `jsonrpclib`, as it uses | |
52 | the same name. | |
53 | ||
54 | ## Summary | |
55 | ||
56 | This library implements the JSON-RPC 2.0 proposed specification in pure Python. | |
57 | It is designed to be as compatible with the syntax of `xmlrpclib` as possible | |
58 | (it extends where possible), so that projects using `xmlrpclib` could easily be | |
59 | modified to use JSON and experiment with the differences. | |
60 | ||
61 | It is backwards-compatible with the 1.0 specification, and supports all of the | |
62 | new proposed features of 2.0, including: | |
63 | ||
64 | - Batch submission (via the `MultiCall` class) | |
65 | - Keyword arguments | |
66 | - Notifications (both in a batch and 'normal') | |
67 | - Class translation using the `__jsonclass__` key. | |
68 | ||
69 | A `SimpleJSONRPCServer` class has been added. It is intended to emulate the | |
70 | `SimpleXMLRPCServer` from the default Python distribution. | |
71 | ||
72 | ## Requirements | |
73 | ||
74 | This library supports `cjson` and `simplejson`, and looks for the parsers in | |
75 | that order (searching first for `cjson`, then for the *built-in* `json` in 2.7+, | |
76 | and then the `simplejson` external library). | |
77 | One of these must be installed to use this library, although if you have a | |
78 | standard distribution of 2.7+, you should already have one. | |
79 | Keep in mind that `cjson` is supposed to be the quickest, I believe, so if you | |
80 | are going for full-on optimization you may want to pick it up. | |
81 | ||
82 | ## Installation | |
83 | ||
84 | You can install this from PyPI with one of the following commands (`sudo` | |
85 | might be required): | |
86 | ||
87 | ``` | |
88 | # Global installation | |
89 | pip install jsonrpclib-pelix | |
90 | ||
91 | # Local installation | |
92 | pip install --user jsonrpclib-pelix | |
93 | ``` | |
94 | ||
95 | Alternatively, you can download the source from the GitHub repository at | |
96 | [tcalmant/jsonrpclib](http://github.com/tcalmant/jsonrpclib) and manually | |
97 | install it with the following commands: | |
98 | ||
99 | ``` | |
100 | git clone git://github.com/tcalmant/jsonrpclib.git | |
101 | cd jsonrpclib | |
102 | python setup.py install | |
103 | ``` | |
104 | ||
105 | ## A note on logging | |
106 | ||
107 | `jsonrpclib-pelix` uses the `logging` module from the standard Python | |
108 | library to trace warnings and errors, but doesn't set it up. | |
109 | As a result, you have to configure the Python logging to print out traces. | |
110 | ||
111 | The easiest way to do it is to add those lines at the beginning of your code: | |
112 | ```python | |
113 | import logging | |
114 | logging.basiConfig() | |
115 | ``` | |
116 | ||
117 | More information can be found in the | |
118 | [`logging` documentation page](https://docs.python.org/3/library/logging.html). | |
119 | ||
120 | ## `SimpleJSONRPCServer` | |
121 | ||
122 | This is identical in usage (or should be) to the `SimpleXMLRPCServer` in the | |
123 | Python standard library. | |
124 | Some of the differences in features are that it obviously supports notification, | |
125 | batch calls, class translation (if left on), etc. | |
126 | ||
127 | **Note:** The import line is slightly different from the regular | |
128 | `SimpleXMLRPCServer`, since the `SimpleJSONRPCServer` is provided by th | |
129 | `jsonrpclib` library. | |
130 | ||
131 | ```python | |
132 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
133 | ||
134 | server = SimpleJSONRPCServer(('localhost', 8080)) | |
135 | server.register_function(pow) | |
136 | server.register_function(lambda x,y: x+y, 'add') | |
137 | server.register_function(lambda x: x, 'ping') | |
138 | server.serve_forever() | |
139 | ``` | |
140 | ||
141 | To start protect the server with SSL, use the following snippet: | |
142 | ||
143 | ```python | |
144 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
145 | import ssl | |
146 | ||
147 | # Setup the SSL socket | |
148 | server = SimpleJSONRPCServer(('localhost', 8080), bind_and_activate=False) | |
149 | server.socket = ssl.wrap_socket(server.socket, certfile='server.pem', | |
150 | server_side=True) | |
151 | server.server_bind() | |
152 | server.server_activate() | |
153 | ||
154 | # ... register functions | |
155 | # Start the server | |
156 | server.serve_forever() | |
157 | ``` | |
158 | ||
159 | ### Notification Thread Pool | |
160 | ||
161 | By default, notification calls are handled in the request handling thread. | |
162 | It is possible to use a thread pool to handle them, by giving it to the server | |
163 | using the `set_notification_pool()` method: | |
164 | ||
165 | ```python | |
166 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
167 | from jsonrpclib.threadpool import ThreadPool | |
168 | ||
169 | # Setup the thread pool: between 0 and 10 threads | |
170 | pool = ThreadPool(max_threads=10, min_threads=0) | |
171 | ||
172 | # Don't forget to start it | |
173 | pool.start() | |
174 | ||
175 | # Setup the server | |
176 | server = SimpleJSONRPCServer(('localhost', 8080)) | |
177 | server.set_notification_pool(pool) | |
178 | ||
179 | # Register methods | |
180 | server.register_function(pow) | |
181 | server.register_function(lambda x,y: x+y, 'add') | |
182 | server.register_function(lambda x: x, 'ping') | |
183 | ||
184 | try: | |
185 | server.serve_forever() | |
186 | finally: | |
187 | # Stop the thread pool (let threads finish their current task) | |
188 | pool.stop() | |
189 | server.set_notification_pool(None) | |
190 | ``` | |
191 | ||
192 | ### Threaded server | |
193 | ||
194 | It is also possible to use a thread pool to handle clients requests, using the | |
195 | `PooledJSONRPCServer` class. | |
196 | By default, this class uses pool of 0 to 30 threads. | |
197 | A custom pool can be given with the `thread_pool` parameter of the class | |
198 | constructor. | |
199 | ||
200 | The notification pool and the request pool are different: by default, a server | |
201 | with a request pool doesn't have a notification pool. | |
202 | ||
203 | ```python | |
204 | from jsonrpclib.SimpleJSONRPCServer import PooledJSONRPCServer | |
205 | from jsonrpclib.threadpool import ThreadPool | |
206 | ||
207 | # Setup the notification and request pools | |
208 | nofif_pool = ThreadPool(max_threads=10, min_threads=0) | |
209 | request_pool = ThreadPool(max_threads=50, min_threads=10) | |
210 | ||
211 | # Don't forget to start them | |
212 | nofif_pool.start() | |
213 | request_pool.start() | |
214 | ||
215 | # Setup the server | |
216 | server = PooledJSONRPCServer(('localhost', 8080), thread_pool=request_pool) | |
217 | server.set_notification_pool(nofif_pool) | |
218 | ||
219 | # Register methods | |
220 | server.register_function(pow) | |
221 | server.register_function(lambda x,y: x+y, 'add') | |
222 | server.register_function(lambda x: x, 'ping') | |
223 | ||
224 | try: | |
225 | server.serve_forever() | |
226 | finally: | |
227 | # Stop the thread pools (let threads finish their current task) | |
228 | request_pool.stop() | |
229 | nofif_pool.stop() | |
230 | server.set_notification_pool(None) | |
231 | ``` | |
232 | ||
233 | ### Unix socket | |
234 | ||
235 | To start a server listening on a Unix socket, you will have to use the | |
236 | following snippet: | |
237 | ||
238 | ```python | |
239 | from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer | |
240 | import os | |
241 | import socket | |
242 | ||
243 | # Set the path to the socket file | |
244 | socket_name = "/tmp/my_socket.socket" | |
245 | ||
246 | # Ensure that the file doesn't exist yet (or an error will be raised) | |
247 | if os.path.exists(socket_name): | |
248 | os.remove(socket_name) | |
249 | ||
250 | try: | |
251 | # Start the server, indicating the socket family | |
252 | # The server will force some flags when in Unix socket mode | |
253 | # (no log request, no reuse address, ...) | |
254 | srv = SimpleJSONRPCServer(socket_name, address_family=socket.AF_UNIX) | |
255 | ||
256 | # ... register methods to the server | |
257 | # Run the server | |
258 | srv.serve_forever() | |
259 | except KeyboardInterrupt: | |
260 | # Shutdown the server gracefully | |
261 | srv.shutdown() | |
262 | srv.server_close() | |
263 | finally: | |
264 | # You should clean up after the server stopped | |
265 | os.remove(socket_name) | |
266 | ``` | |
267 | ||
268 | This feature is tested on Linux during Travis-CI builds. It also has | |
269 | been tested on Windows Subsystem for Linux (WSL) on Windows 10 1809. | |
270 | ||
271 | This feature is not available on "pure" Windows, as it doesn't provide | |
272 | the `AF_UNIX` address family. | |
273 | ||
274 | ## Client Usage | |
275 | ||
276 | This is (obviously) taken from a console session. | |
277 | ||
278 | ```python | |
279 | >>> import jsonrpclib | |
280 | >>> server = jsonrpclib.ServerProxy('http://localhost:8080') | |
281 | >>> server.add(5,6) | |
282 | 11 | |
283 | >>> server.add(x=5, y=10) | |
284 | 15 | |
285 | >>> server._notify.add(5,6) | |
286 | # No result returned... | |
287 | >>> batch = jsonrpclib.MultiCall(server) | |
288 | >>> batch.add(5, 6) | |
289 | >>> batch.ping({'key':'value'}) | |
290 | >>> batch._notify.add(4, 30) | |
291 | >>> results = batch() | |
292 | >>> for result in results: | |
293 | >>> ... print(result) | |
294 | 11 | |
295 | {'key': 'value'} | |
296 | # Note that there are only two responses -- this is according to spec. | |
297 | ||
298 | # Clean up | |
299 | >>> server('close')() | |
300 | ||
301 | # Using client history | |
302 | >>> history = jsonrpclib.history.History() | |
303 | >>> server = jsonrpclib.ServerProxy('http://localhost:8080', history=history) | |
304 | >>> server.add(5,6) | |
305 | 11 | |
306 | >>> print(history.request) | |
307 | {"id": "f682b956-c8e1-4506-9db4-29fe8bc9fcaa", "jsonrpc": "2.0", | |
308 | "method": "add", "params": [5, 6]} | |
309 | >>> print(history.response) | |
310 | {"id": "f682b956-c8e1-4506-9db4-29fe8bc9fcaa", "jsonrpc": "2.0", | |
311 | "result": 11} | |
312 | ||
313 | # Clean up | |
314 | >>> server('close')() | |
315 | ``` | |
316 | ||
317 | If you need 1.0 functionality, there are a bunch of places you can pass | |
318 | that in, although the best is just to give a specific configuration to | |
319 | `jsonrpclib.ServerProxy`: | |
320 | ||
321 | ```python | |
322 | >>> import jsonrpclib | |
323 | >>> jsonrpclib.config.DEFAULT.version | |
324 | 2.0 | |
325 | >>> config = jsonrpclib.config.Config(version=1.0) | |
326 | >>> history = jsonrpclib.history.History() | |
327 | >>> server = jsonrpclib.ServerProxy('http://localhost:8080', config=config, | |
328 | history=history) | |
329 | >>> server.add(7, 10) | |
330 | 17 | |
331 | >>> print(history.request) | |
332 | {"id": "827b2923-5b37-49a5-8b36-e73920a16d32", | |
333 | "method": "add", "params": [7, 10]} | |
334 | >>> print(history.response) | |
335 | {"id": "827b2923-5b37-49a5-8b36-e73920a16d32", "error": null, "result": 17} | |
336 | >>> server('close')() | |
337 | ``` | |
338 | ||
339 | The equivalent `loads` and `dumps` functions also exist, although with | |
340 | minor modifications. | |
341 | The `dumps` arguments are almost identical, but it adds three arguments: | |
342 | `rpcid` for the `id` key, `version` to specify the JSON-RPC compatibility, | |
343 | and `notify` if it's a request that you want to be a notification. | |
344 | ||
345 | Additionally, the `loads` method does not return the params and method like | |
346 | `xmlrpclib`, but instead | |
347 | a.) parses for errors, raising ProtocolErrors, and | |
348 | b.) returns the entire structure of the request / response for manual parsing. | |
349 | ||
350 | ### Unix sockets | |
351 | ||
352 | To connect a JSON-RPC server over a Unix socket, you have to use a specific | |
353 | protocol: `unix+http`. | |
354 | ||
355 | When connecting to a Unix socket in the current working directory, you can use | |
356 | the following syntax: `unix+http://my.socket` | |
357 | ||
358 | When you need to give an absolute path you must use the path part of the URL, | |
359 | the host part will be ignored. For example, you can use this URL to indicate a | |
360 | Unix socket in `/var/lib/daemon.socket`: `unix+http://./var/lib/daemon.socket` | |
361 | ||
362 | **Note:** Currently, only HTTP is supported over a Unix socket. | |
363 | If you want HTTPS support to be implemented, please create an | |
364 | [issue on GitHub](https://github.com/tcalmant/jsonrpclib/issues) | |
365 | ||
366 | ### Additional headers | |
367 | ||
368 | If your remote service requires custom headers in request, you can pass them | |
369 | using the `headers` keyword argument, when creating the `ServerProxy`: | |
370 | ||
371 | ```python | |
372 | >>> import jsonrpclib | |
373 | >>> server = jsonrpclib.ServerProxy("http://localhost:8080", | |
374 | headers={'X-Test' : 'Test'}) | |
375 | ``` | |
376 | ||
377 | You can also put additional request headers only for certain method | |
378 | invocation: | |
379 | ||
380 | ```python | |
381 | >>> import jsonrpclib | |
382 | >>> server = jsonrpclib.Server("http://localhost:8080") | |
383 | >>> with server._additional_headers({'X-Test' : 'Test'}) as test_server: | |
384 | ... test_server.ping(42) | |
385 | ... | |
386 | >>> # X-Test header will be no longer sent in requests | |
387 | ``` | |
388 | ||
389 | Of course `_additional_headers` contexts can be nested as well. | |
390 | ||
391 | ## Class Translation | |
392 | ||
393 | The library supports an *"automatic"* class translation process, although it | |
394 | is turned off by default. | |
395 | This can be devastatingly slow if improperly used, so the following is just a | |
396 | short list of things to keep in mind when using it. | |
397 | ||
398 | - Keep It (the object) Simple Stupid. (for exceptions, keep reading) | |
399 | - Do not require init params (for exceptions, keep reading) | |
400 | - Getter properties without setters could be dangerous (read: not tested) | |
401 | ||
402 | If any of the above are issues, use the `_serialize` method (see usage below). | |
403 | The server and client must **BOTH** have the `use_jsonclass` configuration | |
404 | item on and they must both have access to the same libraries used by the | |
405 | objects for this to work. | |
406 | ||
407 | If you have excessively nested arguments, it would be better to turn off the | |
408 | translation and manually invoke it on specific objects using | |
409 | `jsonrpclib.jsonclass.dump` / `jsonrpclib.jsonclass.load` (since the | |
410 | default behavior recursively goes through attributes and lists/dicts/tuples). | |
411 | ||
412 | * Sample file: `test_obj.py` | |
413 | ||
414 | ```python | |
415 | # This object is /very/ simple, and the system will look through the | |
416 | # attributes and serialize what it can. | |
417 | class TestObj(object): | |
418 | foo = 'bar' | |
419 | ||
420 | # This object requires __init__ params, so it uses the _serialize method | |
421 | # and returns a tuple of init params and attribute values (the init params | |
422 | # can be a dict or a list, but the attribute values must be a dict.) | |
423 | class TestSerial(object): | |
424 | foo = 'bar' | |
425 | def __init__(self, *args): | |
426 | self.args = args | |
427 | def _serialize(self): | |
428 | return (self.args, {'foo':self.foo,}) | |
429 | ``` | |
430 | ||
431 | - Sample usage: | |
432 | ||
433 | ```python | |
434 | >>> import jsonrpclib | |
435 | >>> import test_obj | |
436 | ||
437 | # History is used only to print the serialized form of beans | |
438 | >>> history = jsonrpclib.history.History() | |
439 | >>> testobj1 = test_obj.TestObj() | |
440 | >>> testobj2 = test_obj.TestSerial() | |
441 | >>> server = jsonrpclib.Server('http://localhost:8080', history=history) | |
442 | ||
443 | # The 'ping' just returns whatever is sent | |
444 | >>> ping1 = server.ping(testobj1) | |
445 | >>> ping2 = server.ping(testobj2) | |
446 | ||
447 | >>> print(history.request) | |
448 | {"id": "7805f1f9-9abd-49c6-81dc-dbd47229fe13", "jsonrpc": "2.0", | |
449 | "method": "ping", "params": [{"__jsonclass__": | |
450 | ["test_obj.TestSerial", []], "foo": "bar"} | |
451 | ]} | |
452 | >>> print(history.response) | |
453 | {"id": "7805f1f9-9abd-49c6-81dc-dbd47229fe13", "jsonrpc": "2.0", | |
454 | "result": {"__jsonclass__": ["test_obj.TestSerial", []], "foo": "bar"}} | |
455 | ``` | |
456 | ||
457 | This behavior is turned on by default. | |
458 | To deactivate it, just set the `use_jsonclass` member of a server `Config` to | |
459 | `False`. | |
460 | If you want to use a per-class serialization method, set its name in the | |
461 | `serialize_method` member of a server `Config`. | |
462 | Finally, if you are using classes that you have defined in the implementation | |
463 | (as in, not a separate library), you'll need to add those | |
464 | (on **BOTH** the server and the client) using the `config.classes.add()` method. | |
465 | ||
466 | Feedback on this "feature" is very, VERY much appreciated. | |
467 | ||
468 | ## Tests | |
469 | ||
470 | Tests are an almost-verbatim drop from the JSON-RPC specification 2.0 | |
471 | page. They can be run using *unittest* or *nosetest*: | |
472 | ||
473 | ``` | |
474 | python -m unittest discover tests | |
475 | python3 -m unittest discover tests | |
476 | nosetests tests | |
477 | ``` | |
478 | ||
479 | ## Why JSON-RPC? | |
480 | ||
481 | In my opinion, there are several reasons to choose JSON over XML for RPC: | |
482 | ||
483 | * Much simpler to read (I suppose this is opinion, but I know I'm right. :) | |
484 | * Size / Bandwidth - Main reason, a JSON object representation is just much | |
485 | smaller. | |
486 | * Parsing - JSON should be much quicker to parse than XML. | |
487 | * Easy class passing with `jsonclass` (when enabled) | |
488 | ||
489 | In the interest of being fair, there are also a few reasons to choose XML over | |
490 | JSON: | |
491 | ||
492 | * Your server doesn't do JSON (rather obvious) | |
493 | * Wider XML-RPC support across APIs (can we change this? :)) | |
494 | * Libraries are more established, *i.e.* more stable (Let's change this too) | |
495 | ||
496 | Platform: UNKNOWN | |
497 | Classifier: Development Status :: 5 - Production/Stable | |
498 | Classifier: Intended Audience :: Developers | |
499 | Classifier: License :: OSI Approved :: Apache Software License | |
500 | Classifier: Operating System :: OS Independent | |
501 | Classifier: Programming Language :: Python :: 2.7 | |
502 | Classifier: Programming Language :: Python :: 3 | |
503 | Classifier: Programming Language :: Python :: 3.3 | |
504 | Classifier: Programming Language :: Python :: 3.4 | |
505 | Classifier: Programming Language :: Python :: 3.5 | |
506 | Classifier: Programming Language :: Python :: 3.6 | |
507 | Classifier: Programming Language :: Python :: 3.7 | |
508 | Description-Content-Type: text/markdown |
0 | 0 | LICENSE |
1 | 1 | MANIFEST.in |
2 | 2 | README.md |
3 | pyproject.toml | |
3 | 4 | setup.cfg |
4 | 5 | setup.py |
5 | 6 | jsonrpclib/SimpleJSONRPCServer.py |