{ "info": { "author": "D\u00f3nal McMullan", "author_email": "donal.mcmullan@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Framework :: Twisted", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Communications", "Topic :: System :: Distributed Computing" ], "description": "# wsJsonRpc\n\n**WsJsonRpc** is a [Twisted Python](https://github.com/twisted/twisted) protocol to support [JSON-RPC 2.0](https://www.jsonrpc.org/specification) over [websocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API). The underlying websocket implementation is provided by [Autobahn](https://github.com/crossbario/autobahn-python).\n\n## Features\nBoth the client and server endpoints are fully bidirectional. Once the client connects to the server and the server accepts the connection, either endpoint can initiate requests or notifications on the remote side.\n\nTested versions:\n - Pypy 2.7\n - Pypy 3.6\n - Python 3.7\n\nTest coverage is 92%.\n\n## Usage\nA simple server application should expose at least one method to clients. Here we register a `math.sum` method in the API.\n```python\n#!/usr/bin/env python\n\nimport sys\n\nfrom twisted import logger\nfrom twisted.internet import reactor\n\nfrom wsjsonrpc import factory\n\nlogobserver = logger.textFileLogObserver(sys.stdout)\nlogger.globalLogPublisher.addObserver(logobserver)\n\ndef _sum(protocol, x, y):\n return x + y\n\nif __name__ == \"__main__\":\n\n factory = factory.JsonRpcWebSocketServerFactory(u\"ws://127.0.0.1:8095/wsjsonrpc\")\n factory.registerMethod(\"math.sum\", _sum)\n\n reactor.listenTCP(8095, factory)\n reactor.run()\n```\nThis client calls that remote `math.sum` method, and then exits.\n```python\n#!/usr/bin/env python\n\nimport sys\n\nfrom twisted import logger\nfrom twisted.internet import defer\nfrom twisted.internet import task\n\nfrom wsjsonrpc import factory\n\nlogobserver = logger.textFileLogObserver(sys.stdout)\nlogger.globalLogPublisher.addObserver(logobserver)\nlog = logger.Logger()\n\n@defer.inlineCallbacks\ndef main(reactor):\n\n result = None\n protocol = yield factory.get_client(hostname=\"localhost\", port=8095, path=u\"wsjsonrpc\")\n\n \"\"\"\n Call the 'math.sum' method on our peer and log the result.\n \"\"\"\n result = yield protocol.request(\"math.sum\", [1, 2])\n log.debug(\"sum result: {}\".format(result))\n yield result\n\ntask.react(main)\n```\n## Batch requests\nBoth the client and server will accept and process batch requests. Call `request` or `notify` as often as you need to in the batch context. When you exit the context, the batch will be sent to the peer.\n```python\n#!/usr/bin/env python\n\nimport sys\n\nfrom twisted import logger\nfrom twisted.internet import defer\nfrom twisted.internet import task\n\nfrom wsjsonrpc import factory\n\nlogobserver = logger.textFileLogObserver(sys.stdout)\nlogger.globalLogPublisher.addObserver(logobserver)\nlog = logger.Logger()\n\n@defer.inlineCallbacks\ndef main(reactor):\n\n protocol = yield factory.get_client(hostname=\"localhost\", port=8095, path=u\"wsjsonrpc\")\n\n df = None\n with protocol.batchContext() as batch:\n batch.request(\"math.sum\", [1, 2])\n batch.request(\"math.sum\", [2, 3])\n batch.request(\"math.sum\", [3, 4])\n batch.request(\"math.sum\", [4, 5])\n df = batch.deferredList(consumeErrors=1)\n\n result = yield df\n\n log.debug(\"sum result: {}\".format(result))\n yield result\n\ntask.react(main)\n```\nA batch can be submitted without the context manager. The following batch includes two calls to `notify`. Note that the `notify` method does not return a deferred, as notifications do not generate any response from the server.\n\nThe `defer.gatherResults` call here does not wait for the `notify` calls to complete - as soon as they are dispatched to the peer they are complete.\n```python\n batch = protocol.batch()\n df0 = batch.request(\"math.sum\", [1, 2])\n df1 = batch.request(\"math.sum\", [2, 3])\n batch.notify(\"math.sum\", [3, 4])\n batch.notify(\"math.sum\", [4, 5])\n\n df = defer.gatherResults(batch.deferreds, consumeErrors=1)\n batch.sendBatch()\n yield df\n```\n## Implementing an API\nYour API methods must take `protocol` as their first argument, and all other arguments should be either positional only or keyword only, for example:\n```python\ndef sum_positional(protocol, x, y):\n return x + y\n\ndef sum_keyword(protocol, x=0, y=0):\n return x + y\n```\nThe JSON-RPC spec requires that each request can have one `params` value which MUST be either a `dict`, a `list` or `None`. If WsJsonRpc receives `params` as a list, it expands it into positional arguments:\n```python\n# client: \nprotocol.request(\"math.sum\", [1, 2])\n# server:\nsum_positional(*params)\n```\nIf it receives `params` as a dict, it expands it into keyword arguments:\n```python\n# client: \nprotocol.request(\"math.sum\", {\"x\":1, \"y\":2})\n# server:\nsum_keyword(**params)\n```\n\n## Error handling in your API\nIf an error occurs in your API, you might want to catch and log that exception, and then raise an exception from the `wsjsonrpc.exception.JsonRpcException` family. The WsJsonRpc protocol will extract the correct error code and message from that exception to return a valid error object to the remote peer.\n\nIt's likely that this will be a `JsonRpcInternalError`, but it could also be `JsonRpcMethodNotFound` or a custom subclass of `JsonRpcCustomException` that you have created. See the JSON-RPC documentation of the [Error object](https://www.jsonrpc.org/specification#error_object) for guidance on that.\n\nIf your API raises any other exception, WsJsonRpc will repackage that into a `JsonRpcInternalError` which is probably what you want anyway.\n\n## Authentication\nNote that on the server side, the first argument that is passed into any API method will be the protocol instance itself. This allows you to write methods that authenticate the client (or peer), and then store the client's identity information as an attribute on your custom protocol implementation.\n\nSubsequent API calls will then be able to access that identity information.\n\nAlternatively, if your WsJsonRpc endpoint is one route in a larger web application, you could use the protocol to access the WebSocket request's authentication cookie.\n\n\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://gitlab.com/donalm/wsjsonrpc", "keywords": "websocket,twisted,autobahn,jsonrpc,rpc", "license": "", "maintainer": "", "maintainer_email": "", "name": "wsjsonrpc", "package_url": "https://pypi.org/project/wsjsonrpc/", "platform": "", "project_url": "https://pypi.org/project/wsjsonrpc/", "project_urls": { "Homepage": "https://gitlab.com/donalm/wsjsonrpc" }, "release_url": "https://pypi.org/project/wsjsonrpc/0.0.1/", "requires_dist": [ "autobahn", "twisted" ], "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4", "summary": "JSON-RPC 2.0 over websockets", "version": "0.0.1" }, "last_serial": 5957571, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "237d00960f03022a645ed34f9763d183", "sha256": "3627f8c5cd16ec0faf8129e1fe210b139d07d5eddda889899c0de3f249e4c83c" }, "downloads": -1, "filename": "wsjsonrpc-0.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "237d00960f03022a645ed34f9763d183", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4", "size": 13558, "upload_time": "2019-10-10T23:52:07", "url": "https://files.pythonhosted.org/packages/78/9a/6e043c3a47f6fc1a93a6d7cff67aba9a0eed52b7b49af21f3ff366eae3f4/wsjsonrpc-0.0.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3f95a615fdb76b9edfce563a9658cd76", "sha256": "83e1f0b8c0c3b479614b8a0d528bf76993e9f09b73db70cb3c6a620b24fc26e1" }, "downloads": -1, "filename": "wsjsonrpc-0.0.1-py3.7.egg", "has_sig": false, "md5_digest": "3f95a615fdb76b9edfce563a9658cd76", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4", "size": 12371, "upload_time": "2019-10-10T23:52:09", "url": "https://files.pythonhosted.org/packages/cb/ea/0252ca2beb7e2dc02ebe8a0213d1afa5689640e79a7e1a219843a307d72c/wsjsonrpc-0.0.1-py3.7.egg" }, { "comment_text": "", "digests": { "md5": "dbd9bb09e210212e787d0575608297c7", "sha256": "a4416ddd3219264654f1ae085de538508c1fa9f689515640096eb99b7074d067" }, "downloads": -1, "filename": "wsjsonrpc-0.0.1.tar.gz", "has_sig": false, "md5_digest": "dbd9bb09e210212e787d0575608297c7", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4", "size": 13344, "upload_time": "2019-10-10T23:52:10", "url": "https://files.pythonhosted.org/packages/49/c5/e76bcfe0185d503356940c6fb79a90ebde711a8e77c30d1b554f486f7505/wsjsonrpc-0.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "237d00960f03022a645ed34f9763d183", "sha256": "3627f8c5cd16ec0faf8129e1fe210b139d07d5eddda889899c0de3f249e4c83c" }, "downloads": -1, "filename": "wsjsonrpc-0.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "237d00960f03022a645ed34f9763d183", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4", "size": 13558, "upload_time": "2019-10-10T23:52:07", "url": "https://files.pythonhosted.org/packages/78/9a/6e043c3a47f6fc1a93a6d7cff67aba9a0eed52b7b49af21f3ff366eae3f4/wsjsonrpc-0.0.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3f95a615fdb76b9edfce563a9658cd76", "sha256": "83e1f0b8c0c3b479614b8a0d528bf76993e9f09b73db70cb3c6a620b24fc26e1" }, "downloads": -1, "filename": "wsjsonrpc-0.0.1-py3.7.egg", "has_sig": false, "md5_digest": "3f95a615fdb76b9edfce563a9658cd76", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4", "size": 12371, "upload_time": "2019-10-10T23:52:09", "url": "https://files.pythonhosted.org/packages/cb/ea/0252ca2beb7e2dc02ebe8a0213d1afa5689640e79a7e1a219843a307d72c/wsjsonrpc-0.0.1-py3.7.egg" }, { "comment_text": "", "digests": { "md5": "dbd9bb09e210212e787d0575608297c7", "sha256": "a4416ddd3219264654f1ae085de538508c1fa9f689515640096eb99b7074d067" }, "downloads": -1, "filename": "wsjsonrpc-0.0.1.tar.gz", "has_sig": false, "md5_digest": "dbd9bb09e210212e787d0575608297c7", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4", "size": 13344, "upload_time": "2019-10-10T23:52:10", "url": "https://files.pythonhosted.org/packages/49/c5/e76bcfe0185d503356940c6fb79a90ebde711a8e77c30d1b554f486f7505/wsjsonrpc-0.0.1.tar.gz" } ] }