jsonrpcserver 5.0.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,150 @@
1
+ """What a method returns: Success, Error or InvalidParams.
2
+
3
+ A Result is the "result" or "error" part of a JSON-RPC response object
4
+ (https://www.jsonrpc.org/specification#response_object). The library adds the
5
+ "jsonrpc" and "id" parts.
6
+ """
7
+
8
+ from typing import Any, NamedTuple
9
+
10
+ from oslash.either import Either, Left, Right
11
+
12
+ from .codes import ERROR_INTERNAL_ERROR, ERROR_INVALID_PARAMS, ERROR_METHOD_NOT_FOUND
13
+ from .sentinels import NODATA
14
+ from .utils import warn_if_invalid_error
15
+
16
+ # pylint: disable=missing-class-docstring,missing-function-docstring,invalid-name
17
+
18
+
19
+ class SuccessResult(NamedTuple):
20
+ """The value inside the Result that `Success` returns."""
21
+
22
+ result: Any = None
23
+
24
+ def __repr__(self) -> str:
25
+ return f"SuccessResult({self.result!r})"
26
+
27
+
28
+ class ErrorResult(NamedTuple):
29
+ """The value inside the Result that `Error` and `InvalidParams` return.
30
+
31
+ `data` is `NODATA` when there's no data, so it's left out of the response.
32
+ """
33
+
34
+ code: int
35
+ message: str
36
+ data: Any = NODATA # The spec says this value may be omitted
37
+
38
+ def __repr__(self) -> str:
39
+ return (
40
+ f"ErrorResult(code={self.code!r}, message={self.message!r}, "
41
+ f"data={self.data!r})"
42
+ )
43
+
44
+
45
+ # Union of the two valid result types. oslash's Either takes the success type
46
+ # first, then the error type.
47
+ Result = Either[SuccessResult, ErrorResult]
48
+ """The return type of a method: what `Success`, `Error` and `InvalidParams` give.
49
+
50
+ It's an oslash `Either`: a `Right` holding a `SuccessResult`, or a `Left` holding an
51
+ `ErrorResult`. Use it as the return annotation of your methods. You don't need to
52
+ look inside it.
53
+ """
54
+
55
+
56
+ # Helpers
57
+
58
+
59
+ def MethodNotFoundResult(data: Any) -> ErrorResult:
60
+ return ErrorResult(ERROR_METHOD_NOT_FOUND, "Method not found", data)
61
+
62
+
63
+ def InternalErrorResult(data: Any) -> ErrorResult:
64
+ return ErrorResult(ERROR_INTERNAL_ERROR, "Internal error", data)
65
+
66
+
67
+ def InvalidParamsResult(data: Any = NODATA) -> ErrorResult:
68
+ return ErrorResult(ERROR_INVALID_PARAMS, "Invalid params", data)
69
+
70
+
71
+ # Helpers (the public functions)
72
+
73
+
74
+ def Success(result: Any = None) -> Result:
75
+ """A successful result. Return it from a method.
76
+
77
+ Args:
78
+ result: The value for the response's `result` member. It can be anything
79
+ the serializer handles. The default, None, is sent as `null`.
80
+
81
+ Returns:
82
+ A Result holding the value.
83
+
84
+ Example:
85
+ >>> from jsonrpcserver import Result, Success, dispatch
86
+ >>> def ping() -> Result:
87
+ ... return Success("pong")
88
+ >>> dispatch('{"jsonrpc": "2.0", "method": "ping", "id": 1}', {"ping": ping})
89
+ '{"jsonrpc": "2.0", "result": "pong", "id": 1}'
90
+ """
91
+ return Right(SuccessResult(result))
92
+
93
+
94
+ def Error(code: int, message: str, data: Any = NODATA) -> Result:
95
+ """An error result. Return it from a method to send an error response.
96
+
97
+ It's sent as it is, whatever `debug` is, so don't put secrets in it.
98
+
99
+ Args:
100
+ code: The error code, an integer. The spec reserves -32768 to -32000 for
101
+ its own errors, so pick other numbers for yours.
102
+ message: A short description of the error, as a string.
103
+ data: Extra information for the client, such as details of what went
104
+ wrong. If it isn't given, the response has no `data` member.
105
+
106
+ Returns:
107
+ A Result holding the error.
108
+
109
+ Warns:
110
+ UserWarning: If `code` isn't an int or `message` isn't a str. The error is
111
+ still sent as given. New in 5.0.10.
112
+
113
+ Example:
114
+ >>> from jsonrpcserver import Error, Result, dispatch_to_serializable
115
+ >>> def fail() -> Result:
116
+ ... return Error(1, "It failed", {"reason": "example"})
117
+ >>> request = '{"jsonrpc": "2.0", "method": "fail", "id": 1}'
118
+ >>> dispatch_to_serializable(request, {"fail": fail})["error"]
119
+ {'code': 1, 'message': 'It failed', 'data': {'reason': 'example'}}
120
+ """
121
+ warn_if_invalid_error(code, message, stacklevel=2)
122
+ return Left(ErrorResult(code, message, data))
123
+
124
+
125
+ def InvalidParams(data: Any = NODATA) -> Result:
126
+ """An Invalid params error: `Error(-32602, "Invalid params", data)`.
127
+
128
+ Return it when the arguments have the right shape but a bad value.
129
+ jsonrpcserver already sends this error when the arguments don't fit the
130
+ method's signature.
131
+
132
+ Args:
133
+ data: What was wrong, for the client. If it isn't given, the response has no
134
+ `data` member.
135
+
136
+ Returns:
137
+ A Result holding the error.
138
+
139
+ Example:
140
+ >>> from jsonrpcserver import InvalidParams, Result, Success
141
+ >>> from jsonrpcserver import dispatch_to_serializable
142
+ >>> def rate(stars: int) -> Result:
143
+ ... if stars not in range(1, 6):
144
+ ... return InvalidParams("Stars must be 1 to 5")
145
+ ... return Success()
146
+ >>> request = '{"jsonrpc": "2.0", "method": "rate", "params": [6], "id": 1}'
147
+ >>> dispatch_to_serializable(request, {"rate": rate})["error"]
148
+ {'code': -32602, 'message': 'Invalid params', 'data': 'Stars must be 1 to 5'}
149
+ """
150
+ return Left(InvalidParamsResult(data))
@@ -0,0 +1,28 @@
1
+ """Sentinels - these are used to indicate no data is present.
2
+
3
+ We can't use None, because None may be a valid piece of data.
4
+ """
5
+
6
+ import sys
7
+
8
+
9
+ class Sentinel:
10
+ """Use this class to create a unique object.
11
+
12
+ Has a nicer repr than `object()`.
13
+ """
14
+
15
+ # pylint: disable=too-few-public-methods
16
+ def __init__(self, name: str):
17
+ self.name = name
18
+
19
+ def __repr__(self) -> str:
20
+ return f"<{sys.intern(str(self.name)).rsplit('.', 1)[-1]}>"
21
+
22
+
23
+ NOCONTEXT = Sentinel("NoContext")
24
+ """The default for `context`: don't pass a context to methods."""
25
+ NODATA = Sentinel("NoData")
26
+ """The default for `data` in `Error` and `JsonRpcError`: leave `data` out."""
27
+ NOID = Sentinel("NoId")
28
+ """The id of a notification, which has none."""
@@ -0,0 +1,115 @@
1
+ """A development server for trying out methods, built on Python's http.server.
2
+
3
+ It's meant for trying things out. For production, put dispatch behind a real web
4
+ server or framework.
5
+ """
6
+
7
+ import json
8
+ import logging
9
+ import sys
10
+ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
11
+ from typing import Any
12
+
13
+ from .main import dispatch
14
+ from .response import ParseErrorResponse, to_error_dict
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class RequestHandler(BaseHTTPRequestHandler):
20
+ """Handle HTTP requests"""
21
+
22
+ def log_message(self, format: str, *args: Any) -> None:
23
+ """Log through the logging module instead of writing to sys.stderr.
24
+
25
+ sys.stderr is None in some environments, such as a PyInstaller app built with
26
+ --noconsole. Writing to it there raised an exception that dropped every
27
+ connection without a response (#269).
28
+ """
29
+ logger.info("%s - %s", self.address_string(), format % args)
30
+
31
+ def do_POST(self) -> None:
32
+ """Handle POST request"""
33
+ try:
34
+ length = int(self.headers["Content-Length"])
35
+ except TypeError:
36
+ self.send_error(411, "Content-Length required")
37
+ return
38
+ except ValueError:
39
+ self.send_error(400, "Invalid Content-Length")
40
+ return
41
+ if length < 0:
42
+ self.send_error(400, "Invalid Content-Length")
43
+ return
44
+ body = self.rfile.read(length)
45
+ try:
46
+ request = body.decode("utf-8")
47
+ except UnicodeDecodeError as exc:
48
+ response = json.dumps(to_error_dict(ParseErrorResponse(str(exc))))
49
+ else:
50
+ response = dispatch(request)
51
+ if response:
52
+ self.send_response(200)
53
+ self.send_header("Content-Type", "application/json")
54
+ self.send_header("Content-Length", str(len(response.encode())))
55
+ self.end_headers()
56
+ self.wfile.write(response.encode())
57
+ else:
58
+ # A notification, or a batch of only notifications. There's nothing to send.
59
+ self.send_response(204)
60
+ self.end_headers()
61
+
62
+
63
+ def listening_message(name: str, port: int) -> str:
64
+ """The line serve() shows when it starts."""
65
+ if name in ("", "0.0.0.0"):
66
+ return (
67
+ f" * Listening on port {port} on every network interface. This is a "
68
+ 'development server. Use serve("localhost", ...) to accept only local '
69
+ "connections."
70
+ )
71
+ return f" * Listening on http://{name}:{port}/. This is a development server."
72
+
73
+
74
+ def serve(name: str = "", port: int = 5000) -> None:
75
+ """Serve the methods registered with `@method` over HTTP. For development only.
76
+
77
+ It answers POST requests on any path with `dispatch`, sends 204 No Content for a
78
+ notification, and runs each request in its own thread. It runs until the process
79
+ is stopped, for example with Ctrl+C.
80
+
81
+ When it starts, it logs where it's listening on the `jsonrpcserver.server`
82
+ logger. If logging isn't configured, it writes that line to stderr instead
83
+ (new in 5.0.10). Each request is logged at INFO level.
84
+
85
+ It has no TLS, no authentication and no request size limit, and it doesn't pass
86
+ `max_batch_size`. Put `dispatch` behind a real web server or framework in
87
+ production.
88
+
89
+ Args:
90
+ name: The host name or address to listen on. The default, "", listens on
91
+ every network interface, so other machines can connect. Pass "localhost"
92
+ to accept only local connections.
93
+ port: The port to listen on.
94
+
95
+ Example:
96
+ ```python
97
+ from jsonrpcserver import Result, Success, method, serve
98
+
99
+ @method
100
+ def ping() -> Result:
101
+ return Success("pong")
102
+
103
+ serve("localhost", 8000)
104
+ ```
105
+ """
106
+ httpd = ThreadingHTTPServer((name, port), RequestHandler)
107
+ try:
108
+ message = listening_message(name, port)
109
+ logger.info("%s", message)
110
+ if not logger.hasHandlers() and sys.stderr is not None:
111
+ # logging isn't configured, so the line above went nowhere.
112
+ print(message, file=sys.stderr, flush=True)
113
+ httpd.serve_forever()
114
+ finally:
115
+ httpd.server_close()
jsonrpcserver/utils.py ADDED
@@ -0,0 +1,51 @@
1
+ """Utility functions"""
2
+
3
+ import warnings
4
+ from functools import reduce
5
+ from typing import Any, Callable, List, TypeVar, cast
6
+
7
+ from oslash.either import Either, Right
8
+
9
+ T = TypeVar("T")
10
+
11
+ # pylint: disable=invalid-name
12
+
13
+
14
+ def identity(x: Any) -> Any:
15
+ """Returns the argument."""
16
+ return x
17
+
18
+
19
+ def compose(*funcs: Callable[..., Any]) -> Callable[..., Any]:
20
+ """Compose two or more functions producing a single composite function."""
21
+ return reduce(lambda f, g: lambda *a, **kw: f(g(*a, **kw)), funcs)
22
+
23
+
24
+ def make_list(x: Any) -> List[Any]:
25
+ """Puts a value into a list if it's not already."""
26
+ # pyright can't tell the element type of a list narrowed from Any.
27
+ return x if isinstance(x, list) else [x] # pyright: ignore[reportUnknownVariableType]
28
+
29
+
30
+ def warn_if_invalid_error(code: Any, message: Any, stacklevel: int) -> None:
31
+ """Warn if an error's code or message breaks the JSON-RPC spec.
32
+
33
+ The spec says code MUST be an integer and message SHOULD be a string. These used to
34
+ be sent as given, which some clients can't parse. It's a warning, not an error,
35
+ so existing code keeps working.
36
+ """
37
+ if isinstance(code, bool) or not isinstance(code, int):
38
+ warnings.warn(
39
+ f"JSON-RPC error codes must be integers, not {code!r}",
40
+ stacklevel=stacklevel + 1,
41
+ )
42
+ if not isinstance(message, str):
43
+ warnings.warn(
44
+ f"JSON-RPC error messages should be strings, not {message!r}",
45
+ stacklevel=stacklevel + 1,
46
+ )
47
+
48
+
49
+ def unwrap(either: Either[T, Any]) -> T:
50
+ """The value inside a Right. Only call this once you know it isn't a Left."""
51
+ return cast("Right[T, Any]", either)._value
@@ -0,0 +1,163 @@
1
+ Metadata-Version: 2.5
2
+ Name: jsonrpcserver
3
+ Version: 5.0.10
4
+ Summary: Process incoming JSON-RPC 2.0 requests in Python
5
+ Keywords: json-rpc,jsonrpc,json-rpc-2.0,rpc,server
6
+ Author-email: Beau Barker <beau@explodinglabs.com>
7
+ Maintainer-email: Synapse Research <ben@synapsereality.io>
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ License-Expression: MIT
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable
24
+ Classifier: Programming Language :: Python :: Implementation :: CPython
25
+ Classifier: Topic :: Internet
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Classifier: Typing :: Typed
28
+ License-File: LICENSE
29
+ Requires-Dist: jsonschema<5
30
+ Requires-Dist: oslash<1
31
+ Requires-Dist: hypothesis ; extra == "test"
32
+ Requires-Dist: pytest ; extra == "test"
33
+ Requires-Dist: pytest-asyncio ; extra == "test"
34
+ Requires-Dist: pytest-cov ; extra == "test"
35
+ Project-URL: Changelog, https://bensynapse.github.io/jsonrpcserver/changelog/
36
+ Project-URL: Documentation, https://bensynapse.github.io/jsonrpcserver/
37
+ Project-URL: Homepage, https://bensynapse.github.io/jsonrpcserver/
38
+ Project-URL: Issues, https://github.com/bensynapse/jsonrpcserver/issues
39
+ Project-URL: Repository, https://github.com/bensynapse/jsonrpcserver
40
+ Provides-Extra: test
41
+ Import-Name: jsonrpcserver
42
+
43
+ <p align="center">
44
+ <a href="https://pypi.org/project/jsonrpcserver/"><img src="https://img.shields.io/pypi/v/jsonrpcserver.svg" alt="PyPI version" /></a>
45
+ <a href="https://github.com/bensynapse/jsonrpcserver/actions/workflows/ci.yml"><img src="https://github.com/bensynapse/jsonrpcserver/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
46
+ <a href="https://bensynapse.github.io/jsonrpcserver/#install"><img src="https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue" alt="Python 3.8 to 3.14" /></a>
47
+ <a href="https://pypistats.org/packages/jsonrpcserver"><img src="https://img.shields.io/pypi/dm/jsonrpcserver" alt="Downloads per month" /></a>
48
+ <a href="https://github.com/bensynapse/jsonrpcserver/blob/main/LICENSE"><img src="https://img.shields.io/github/license/bensynapse/jsonrpcserver" alt="License: MIT" /></a>
49
+ </p>
50
+
51
+ <p align="center">
52
+ <img alt="jsonrpcserver" src="https://raw.githubusercontent.com/bensynapse/jsonrpcserver/main/logo.png" />
53
+ </p>
54
+
55
+ <p align="center">
56
+ <i>Process incoming JSON-RPC 2.0 requests in Python</i>
57
+ </p>
58
+
59
+ <p align="center">
60
+ <a href="https://bensynapse.github.io/jsonrpcserver/">Documentation</a> |
61
+ <a href="https://bensynapse.github.io/jsonrpcserver/reference/">API reference</a> |
62
+ <a href="https://bensynapse.github.io/jsonrpcserver/examples/">Examples</a> |
63
+ <a href="https://bensynapse.github.io/jsonrpcserver/changelog/">Changelog</a> |
64
+ <a href="https://bensynapse.github.io/jsonrpcserver/migration/">Migration</a>
65
+ </p>
66
+
67
+ jsonrpcserver takes a [JSON-RPC 2.0](https://www.jsonrpc.org/specification)
68
+ request, calls your Python function and gives you the response to send back.
69
+ It leaves the networking to you, so it fits into whatever server you already
70
+ have.
71
+
72
+ ## Features
73
+
74
+ - Works with any framework or transport. The docs have tested examples for
75
+ http.server, Flask, Werkzeug, Django, FastAPI, aiohttp, Sanic, Tornado,
76
+ websockets, ZeroMQ and Socket.IO.
77
+ - Sync and async: `dispatch` and `async_dispatch`. With `async_dispatch`, the
78
+ requests in a batch run concurrently.
79
+ - Follows the spec for batches, notifications and errors, and checks each
80
+ request's params against your function's signature.
81
+ - Keeps exception messages out of responses and in your logs, and limits
82
+ batch size with `max_batch_size` (from 5.0.10).
83
+ - Typed: ships `py.typed`, and `@method` keeps your functions' signatures for
84
+ mypy and pyright.
85
+ - Safe to call from several threads, and tested on Python 3.8 to 3.14,
86
+ including free-threaded 3.14t.
87
+
88
+ ## Install
89
+
90
+ ```sh
91
+ pip install jsonrpcserver
92
+ ```
93
+
94
+ If you're on 5.0.9 or older, upgrade: those versions send exception messages
95
+ to clients. The
96
+ [changelog](https://bensynapse.github.io/jsonrpcserver/changelog/) lists what
97
+ changed in 5.0.10.
98
+
99
+ ## Quickstart
100
+
101
+ Save this as `server.py` and run it with `python server.py`:
102
+
103
+ <!-- skip: it runs forever. docs/examples/check_examples.py runs the same file. -->
104
+ ```python
105
+ from jsonrpcserver import Result, Success, method, serve
106
+
107
+
108
+ @method
109
+ def ping() -> Result:
110
+ return Success("pong")
111
+
112
+
113
+ if __name__ == "__main__":
114
+ serve("localhost", 8000)
115
+ ```
116
+
117
+ Then send it a request from another terminal:
118
+
119
+ ```sh
120
+ curl -s -H 'Content-Type: application/json' -d '{"jsonrpc": "2.0", "method": "ping", "id": 1}' http://localhost:8000/
121
+ ```
122
+
123
+ Output:
124
+
125
+ ```text
126
+ {"jsonrpc": "2.0", "result": "pong", "id": 1}
127
+ ```
128
+
129
+ `serve()` is a small server for trying things out. In your own framework,
130
+ pass the request body to `dispatch` and send back the string it returns. The
131
+ [examples](https://bensynapse.github.io/jsonrpcserver/examples/) show how.
132
+
133
+ ## Security
134
+
135
+ Before you put a server on the internet, pass `max_batch_size` to `dispatch`
136
+ and limit the request body size in your framework. Run 5.0.10 or later. The
137
+ [Security page](https://bensynapse.github.io/jsonrpcserver/security/)
138
+ explains why.
139
+
140
+ ## Documentation
141
+
142
+ - [Documentation](https://bensynapse.github.io/jsonrpcserver/): guides,
143
+ framework examples and the
144
+ [API reference](https://bensynapse.github.io/jsonrpcserver/reference/).
145
+ - [Migrating from 4.x](https://bensynapse.github.io/jsonrpcserver/migration/):
146
+ methods must now return `Success(value)`. A 4.x method that returns a plain
147
+ value still runs, but the client gets an Internal error.
148
+ - [Contributing](https://github.com/bensynapse/jsonrpcserver/blob/main/CONTRIBUTING.md)
149
+ and the
150
+ [security policy](https://github.com/bensynapse/jsonrpcserver/blob/main/SECURITY.md).
151
+ - [License](https://github.com/bensynapse/jsonrpcserver/blob/main/LICENSE): MIT.
152
+
153
+ ## See also
154
+
155
+ [jsonrpcclient](https://bensynapse.github.io/jsonrpcclient/)
156
+ ([GitHub](https://github.com/bensynapse/jsonrpcclient)) creates JSON-RPC
157
+ requests and parses the responses in Python. It's the client-side companion
158
+ to this library, and its quickstart talks to the server above.
159
+
160
+ ## Credits
161
+
162
+ Created by Beau Barker. Maintained by [Synapse Research](https://synapsereality.io).
163
+
@@ -0,0 +1,20 @@
1
+ jsonrpcserver/__init__.py,sha256=I0vyGlCzULRQfDmNYgSTEq75mh5d74raO6ulAEcPpE0,1143
2
+ jsonrpcserver/async_dispatcher.py,sha256=fGm58YijUQ4De1FJHSvVQz7pcKhBbQRTRL3CmZxZ-qA,6254
3
+ jsonrpcserver/async_main.py,sha256=l91jnWHcxR7pCLsOkOaEtRrSSEcR8Wn360JnNK_7K1U,6698
4
+ jsonrpcserver/codes.py,sha256=chdfOcuTHbv-rVRe27aehsr-y2x4Opax0p7wSDJlCiw,748
5
+ jsonrpcserver/dispatcher.py,sha256=-9JCmFjMS6_TZOf9Mgob-P6a2J5GhOh0LfWTGvwCYTA,16947
6
+ jsonrpcserver/exceptions.py,sha256=7I2krzLJOrifighXVXxl9exwgajLgy6dJqAyrCkZZcM,1439
7
+ jsonrpcserver/main.py,sha256=HjeQChWFqw4RLd0NjurUCEtjJq8VqFdO1mro1EgvNTw,11907
8
+ jsonrpcserver/methods.py,sha256=hiEzP5x5Liw8VhemqFz9lRuJoJk9Xq9vHKkhYT-bzxU,3142
9
+ jsonrpcserver/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ jsonrpcserver/request-schema.json,sha256=JY5j2q8hU6hbJBdyv7I932YDw5AjuMmw-v3KUDVyvTc,1277
11
+ jsonrpcserver/request.py,sha256=R2bChmGSDH3gYD2JdegQbzpZjdop_wOqLWSQzZqm9nE,398
12
+ jsonrpcserver/response.py,sha256=IWIxpObs4l07IdMezlMNHFX0S0lF4EwTaS3H8UK8tpo,6053
13
+ jsonrpcserver/result.py,sha256=wp3iwsB1Jnq0mCs0seghm4SJnQ9Y3aomuAGbHpSux3k,5129
14
+ jsonrpcserver/sentinels.py,sha256=fwL9_C57DhuQkxp2pW9T4fi0IcH6nCHHyfaaNcjBXSU,741
15
+ jsonrpcserver/server.py,sha256=gnpEY1YhCyiJh6EwooSdlEWq8vfyHkjXz-WFcjqfVw4,4210
16
+ jsonrpcserver/utils.py,sha256=aGXOeAmNZNSertL3d87mgWAt4vX48EfTL-E636mWR5o,1683
17
+ jsonrpcserver-5.0.10.dist-info/licenses/LICENSE,sha256=_FwZBYAN8tqi6GIMUwE3E9UUG06-g-gEjYUrCr7c2Gg,1106
18
+ jsonrpcserver-5.0.10.dist-info/WHEEL,sha256=rLJE8OLPBZbz7tqxs1yD-wA-ADa1Ec5PuYU-Y_PNWDI,81
19
+ jsonrpcserver-5.0.10.dist-info/METADATA,sha256=u9ZXOJ5upoDzx9MMRjHt_RjzsTEmuk1Ytzrp4uLebyo,6830
20
+ jsonrpcserver-5.0.10.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: flit 4.2.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Beau Barker (beau at explodinglabs.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
+ of the Software, and to permit persons to whom the Software is furnished to do
10
+ so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.