Skip to content

Commit 8c23c46

Browse files
authored
Replace readthedocs with mkdocs (#282)
* Add docs back * Replace readthedocs with mkdocs
1 parent 389959d commit 8c23c46

File tree

9 files changed

+327
-15
lines changed

9 files changed

+327
-15
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<img
22
alt="jsonrpcserver"
33
style="margin: 0 auto;"
4-
src="https://github.com/explodinglabs/jsonrpcserver/blob/main/docs/logo.png?raw=true"
4+
src="https://github.com/explodinglabs/jsonrpcserver/blob/main/logo.png?raw=true"
55
/>
66

77
![PyPI](https://img.shields.io/pypi/v/jsonrpcserver.svg)
@@ -16,7 +16,7 @@ pip install jsonrpcserver
1616
```
1717

1818
```python
19-
from jsonrpcserver import method, serve, Ok, Result
19+
from jsonrpcserver import method, Result, Ok
2020

2121
@method
2222
def ping() -> Result:

docs/async.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
Async dispatch is supported.
2+
3+
```python
4+
from jsonrpcserver import async_dispatch, async_method, Ok, Result
5+
6+
@async_method
7+
async def ping() -> Result:
8+
return Ok("pong")
9+
10+
await async_dispatch('{"jsonrpc": "2.0", "method": "ping", "id": 1}')
11+
```
12+
13+
Some reasons to use this:
14+
15+
- Use it with an asynchronous protocol like sockets or message queues.
16+
- `await` long-running functions from your method.
17+
- Batch requests are dispatched concurrently.
18+
19+
## Notifications
20+
21+
Notifications are requests without an `id`. We should not respond to
22+
notifications, so jsonrpcserver gives an empty string to signify there is *no
23+
response*.
24+
25+
```python
26+
>>> await async_dispatch('{"jsonrpc": "2.0", "method": "ping"}')
27+
''
28+
```
29+
30+
If the response is an empty string, don't send it.
31+
32+
```python
33+
if response := dispatch(request):
34+
send(response)
35+
```
36+
37+
```{note}
38+
A synchronous protocol like HTTP requires a response no matter what, so we can
39+
send back the empty string. However with async protocols, we have the choice of
40+
responding or not.
41+
```

docs/dispatch.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Dispatch
2+
3+
The `dispatch` function processes a JSON-RPC request, attempting to call the method(s)
4+
and gives a JSON-RPC response.
5+
6+
```python
7+
>>> dispatch('{"jsonrpc": "2.0", "method": "ping", "id": 1}')
8+
'{"jsonrpc": "2.0", "result": "pong", "id": 1}'
9+
```
10+
11+
It's a pure function; it will always give you a JSON-RPC response. No exceptions will be
12+
raised.
13+
14+
[See how dispatch is used in different frameworks.](examples)
15+
16+
## Optional parameters
17+
18+
The `dispatch` function takes a request as its argument, and also has some optional
19+
parameters that allow you to customise how it works.
20+
21+
### methods
22+
23+
This lets you specify the methods to dispatch to. It's an alternative to using
24+
the `@method` decorator. The value should be a dict mapping function names to
25+
functions.
26+
27+
```python
28+
def ping():
29+
return Ok("pong")
30+
31+
dispatch(request, methods={"ping": ping})
32+
```
33+
34+
Default is `global_methods`, which is an internal dict populated by the
35+
`@method` decorator.
36+
37+
### context
38+
39+
If specified, this will be the first argument to all methods.
40+
41+
```python
42+
@method
43+
def greet(context, name):
44+
return Ok(f"Hello {context}")
45+
46+
>>> dispatch('{"jsonrpc": "2.0", "method": "greet", "params": ["Beau"], "id": 1}', context="Beau")
47+
'{"jsonrpc": "2.0", "result": "Hello Beau", "id": 1}'
48+
```
49+
50+
### deserializer
51+
52+
A function that parses the JSON request string. Default is `json.loads`.
53+
54+
```python
55+
dispatch(request, deserializer=ujson.loads)
56+
```
57+
58+
### jsonrpc_validator
59+
60+
A function that validates the request once the JSON string has been parsed. The
61+
function should raise an exception (any exception) if the request doesn't match
62+
the JSON-RPC spec (https://www.jsonrpc.org/specification). Default is
63+
`default_jsonrpc_validator` which uses Jsonschema to validate requests against
64+
a schema.
65+
66+
To disable JSON-RPC validation, pass `jsonrpc_validator=lambda _: None`, which
67+
will improve performance because this validation takes around half the dispatch
68+
time.
69+
70+
### args_validator
71+
72+
A function that validates a request's parameters against the signature of the
73+
Python function that will be called for it. Note this should not validate the
74+
_values_ of the parameters, it should simply ensure the parameters match the
75+
Python function's signature. For reference, see the `validate_args` function in
76+
`dispatcher.py`, which is the default `args_validator`.
77+
78+
### serializer
79+
80+
A function that serializes the response string. Default is `json.dumps`.
81+
82+
```python
83+
dispatch(request, serializer=ujson.dumps)
84+
```

docs/faq.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
## How to disable schema validation?
2+
3+
Validating requests is costly - roughly 40% of dispatching time is spent on schema validation.
4+
If you know the incoming requests are valid, you can disable the validation for better
5+
performance.
6+
7+
```python
8+
dispatch(request, validator=lambda _: None)
9+
```
10+
11+
## Which HTTP status code to respond with?
12+
13+
I suggest:
14+
15+
```python
16+
200 if response else 204
17+
```
18+
19+
If the request was a notification, `dispatch` will give you an empty string. So
20+
since there's no http body, use status code 204 - no content.
21+
22+
## How to rename a method
23+
24+
Use `@method(name="new_name")`.
25+
26+
Or use the dispatch function's [methods
27+
parameter](https://www.jsonrpcserver.com/en/latest/dispatch.html#methods).
28+
29+
## How to get the response in other forms?
30+
31+
Instead of `dispatch`, use:
32+
33+
- `dispatch_to_serializable` to get the response as a dict.
34+
- `dispatch_to_response` to get the response as a namedtuple (either a
35+
`SuccessResponse` or `ErrorResponse`, these are defined in
36+
[response.py](https://github.com/explodinglabs/jsonrpcserver/blob/main/jsonrpcserver/response.py)).
37+
38+
For these functions, if the request was a batch, you'll get a list of
39+
responses. If the request was a notification, you'll get `None`.

docs/index.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Jsonrpcserver
2+
3+
Jsonrpcserver processes JSON-RPC requests.
4+
5+
## Quickstart
6+
7+
Install jsonrpcserver:
8+
```python
9+
pip install jsonrpcserver
10+
```
11+
12+
Create a `server.py`:
13+
14+
```python
15+
from jsonrpcserver import method, serve, Ok
16+
17+
@method
18+
def ping():
19+
return Ok("pong")
20+
21+
if __name__ == "__main__":
22+
serve()
23+
```
24+
25+
Start the server:
26+
```sh
27+
$ python server.py
28+
```
29+
30+
Send a request:
31+
```sh
32+
$ curl -X POST http://localhost:5000 -d '{"jsonrpc": "2.0", "method": "ping", "id": 1}'
33+
{"jsonrpc": "2.0", "result": "pong", "id": 1}
34+
```
35+
36+
`serve` starts a basic development server. Do not use it in a production deployment. Use
37+
a production WSGI server instead, with jsonrpcserver's [dispatch](dispatch) function.

docs/methods.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Methods
2+
3+
Methods are functions that can be called by a JSON-RPC request.
4+
5+
## Writing methods
6+
7+
To write a method, decorate a function with `@method`:
8+
9+
```python
10+
from jsonrpcserver import method, Error, Ok, Result
11+
12+
@method
13+
def ping() -> Result:
14+
return Ok("pong")
15+
```
16+
17+
If you don't need to respond with any value simply `return Ok()`.
18+
19+
## Responses
20+
21+
Methods return either `Ok` or `Error`. These are the [JSON-RPC response
22+
objects](https://www.jsonrpc.org/specification#response_object) (excluding the
23+
`jsonrpc` and `id` parts). `Error` takes a code, message, and optionally
24+
'data'.
25+
26+
```python
27+
@method
28+
def test() -> Result:
29+
return Error(1, "There was a problem")
30+
```
31+
32+
Alternatively, raise a `JsonRpcError`, which takes the same arguments as `Error`.
33+
34+
## Parameters
35+
36+
Methods can accept arguments.
37+
38+
```python
39+
@method
40+
def hello(name: str) -> Result:
41+
return Ok("Hello " + name)
42+
```
43+
44+
Testing it:
45+
46+
```sh
47+
$ curl -X POST http://localhost:5000 -d '{"jsonrpc": "2.0", "method": "hello", "params": ["Beau"], "id": 1}'
48+
{"jsonrpc": "2.0", "result": "Hello Beau", "id": 1}
49+
```
50+
51+
## Invalid params
52+
53+
A common error response is *invalid params*.
54+
The JSON-RPC error code for this is **-32602**. A shortcut, *InvalidParams*, is
55+
included so you don't need to remember that.
56+
57+
```python
58+
from jsonrpcserver import dispatch, method, InvalidParams, Ok, Result
59+
60+
@method
61+
def within_range(num: int) -> Result:
62+
if num not in range(1, 5):
63+
return InvalidParams("Value must be 1-5")
64+
return Ok()
65+
```
66+
67+
This is the same as saying
68+
```python
69+
return Error(-32602, "Invalid params", "Value must be 1-5")
70+
```

jsonrpcserver/server.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,27 @@
22
http.server module.
33
"""
44

5-
import logging
65
from http.server import BaseHTTPRequestHandler, HTTPServer
76

87
from .main import dispatch
98

109

1110
class RequestHandler(BaseHTTPRequestHandler):
12-
"""Handle HTTP requests"""
13-
1411
def do_POST(self) -> None: # pylint: disable=invalid-name
15-
"""Handle POST request"""
16-
response = dispatch(
17-
self.rfile.read(int(str(self.headers["Content-Length"]))).decode()
18-
)
12+
request = self.rfile.read(int(str(self.headers["Content-Length"]))).decode()
13+
response = dispatch(request)
1914
if response is not None:
2015
self.send_response(200)
2116
self.send_header("Content-type", "application/json")
2217
self.end_headers()
23-
self.wfile.write(str(response).encode())
18+
self.wfile.write(response.encode())
2419

2520

2621
def serve(name: str = "", port: int = 5000) -> None:
27-
"""A simple function to serve HTTP requests"""
28-
logging.info(" * Listening on port %s", port)
22+
httpd = HTTPServer((name, port), RequestHandler)
2923
try:
30-
httpd = HTTPServer((name, port), RequestHandler)
3124
httpd.serve_forever()
3225
except KeyboardInterrupt:
3326
pass
34-
except Exception:
27+
finally:
3528
httpd.shutdown()
36-
raise

logo.png

17.2 KB
Loading

mkdocs.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
markdown_extensions:
2+
- pymdownx.highlight:
3+
pygments_lang_class: true
4+
- pymdownx.inlinehilite
5+
- pymdownx.snippets
6+
- pymdownx.details
7+
- pymdownx.superfences
8+
- pymdownx.mark
9+
nav:
10+
- Home: 'index.md'
11+
- 'methods.md'
12+
- 'dispatch.md'
13+
- 'async.md'
14+
- 'faq.md'
15+
- 'examples.md'
16+
repo_name: jsonrpcserver
17+
repo_url: https://github.com/explodinglabs/jsonrpcserver
18+
site_author: Exploding Labs
19+
site_description: Welcome to the documentation for Jsonrcpcserver.
20+
site_name: Jsonrpcserver
21+
site_url: https://www.jsonrpcserver.com/
22+
theme:
23+
features:
24+
- content.code.copy
25+
- navigation.footer
26+
- navigation.tabs
27+
- toc.integrate
28+
name: material
29+
palette:
30+
# Palette toggle for automatic mode
31+
- media: "(prefers-color-scheme)"
32+
toggle:
33+
icon: material/brightness-auto
34+
name: Switch to light mode
35+
# Palette toggle for light mode
36+
- media: "(prefers-color-scheme: light)"
37+
scheme: default
38+
toggle:
39+
icon: material/brightness-7
40+
name: Switch to dark mode
41+
# Palette toggle for dark mode
42+
- media: "(prefers-color-scheme: dark)"
43+
scheme: slate
44+
toggle:
45+
icon: material/brightness-4
46+
name: Switch to system preference
47+
extra:
48+
version:
49+
provider: mike

0 commit comments

Comments
 (0)