Skip to content

Commit 51cc241

Browse files
committed
Initial commit moved from jsonrpcserver repo
0 parents  commit 51cc241

File tree

10 files changed

+342
-0
lines changed

10 files changed

+342
-0
lines changed

.github/workflows/deploy-docs.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# ⚠️ This workflow deploys docs to explodinglabs.com
2+
# It is active only in the official repo. Forks won't trigger deployment.
3+
name: Build and Deploy MkDocs
4+
5+
on:
6+
push:
7+
branches:
8+
- main # or your main development branch
9+
10+
jobs:
11+
deploy:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- name: Checkout source repo
16+
uses: actions/checkout@v3
17+
18+
- name: Set up Python
19+
uses: actions/setup-python@v4
20+
with:
21+
python-version: 3.x
22+
23+
- name: Install dependencies
24+
run: pip install mkdocs mkdocs-material
25+
26+
- name: Build MkDocs site
27+
run: mkdocs build
28+
29+
- name: Clone target repo
30+
run: |
31+
git clone https://x-access-token:${{ secrets.TARGET_REPO_PAT }}@github.com/explodinglabs/explodinglabs.com.git target-repo
32+
rm -rf target-repo/$(basename "$GITHUB_REPOSITORY")
33+
mkdir -p target-repo/$(basename "$GITHUB_REPOSITORY")
34+
cp -a site/. target-repo/$(basename "$GITHUB_REPOSITORY")/
35+
env:
36+
GIT_AUTHOR_NAME: GitHub Actions
37+
GIT_COMMITTER_NAME: GitHub Actions
38+
GIT_AUTHOR_EMAIL: [email protected]
39+
GIT_COMMITTER_EMAIL: [email protected]
40+
41+
- name: Commit and push
42+
run: |
43+
cd target-repo
44+
git config user.name "Exploding Labs Bot"
45+
git config user.email "[email protected]"
46+
git add .
47+
git commit -m "Update site from docs source repo" || echo "No changes to commit"
48+
git push

docs/async.md

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

docs/dispatch.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Dispatch
2+
3+
The `dispatch` function takes a JSON-RPC request, calls the appropriate method
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+
[See how dispatch is used in different frameworks.](examples.md)
12+
13+
## Optional parameters
14+
15+
### methods
16+
17+
This lets you specify a group of methods to dispatch to. It's an alternative to
18+
using the `@method` decorator. The value should be a dict mapping function
19+
names to functions.
20+
21+
```python
22+
def ping():
23+
return Success("pong")
24+
25+
dispatch(request, methods={"ping": ping})
26+
```
27+
28+
Default is `global_methods`, which is an internal dict populated by the
29+
`@method` decorator.
30+
31+
### context
32+
33+
If specified, this will be the first argument to all methods.
34+
35+
```python
36+
@method
37+
def greet(context, name):
38+
return Success(context + " " + name)
39+
40+
>>> dispatch('{"jsonrpc": "2.0", "method": "greet", "params": ["Beau"], "id": 1}', context="Hello")
41+
'{"jsonrpc": "2.0", "result": "Hello Beau", "id": 1}'
42+
```
43+
44+
### deserializer
45+
46+
A function that parses the request string. Default is `json.loads`.
47+
48+
```python
49+
dispatch(request, deserializer=ujson.loads)
50+
```
51+
52+
### serializer
53+
54+
A function that serializes the response string. Default is `json.dumps`.
55+
56+
```python
57+
dispatch(request, serializer=ujson.dumps)
58+
```
59+
60+
### validator
61+
62+
A function that validates the request once the json has been parsed.
63+
The function should raise an exception (any exception) if the request
64+
doesn't match the JSON-RPC spec. Default is `default_validator` which
65+
validates the request against a schema.

docs/examples.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Examples
2+
3+
Examples have moved to the [Community Wiki](https://github.com/explodinglabs/jsonrpcserver/wiki).

docs/faq.md

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

docs/images/logo.png

17.2 KB
Loading

docs/index.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
hide:
3+
- toc
4+
---
5+
6+
<style>
7+
.md-content__inner h1:first-of-type {
8+
display: none;
9+
}
10+
</style>
11+
12+
![jsonrpcserver](images/logo.png)
13+
14+
_Process incoming JSON-RPC requests in Python._
15+
16+
Jump to:
17+
[GitHub](https://github.com/explodinglabs/jsonrpcserver) | [Community Wiki](https://github.com/explodinglabs/jsonrpcserver/wiki)
18+
19+
## Documentation
20+
21+
- [Installation](installation.md)
22+
- [Methods](methods.md)
23+
- [Dispatch](dispatch.md)
24+
- [Async](async.md)
25+
- [Faq](faq.md)
26+
- [Examples](examples.md)

docs/installation.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Installation
2+
3+
Install with Pip:
4+
5+
```sh
6+
pip install jsonrpcserver
7+
```

docs/methods.md

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

mkdocs.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
site_name: jsonrpcserver
2+
site_url: https://explodinglabs.com/jsonrpcserver/
3+
repo_url: https://github.com/explodinglabs/jsonrpcserver
4+
theme:
5+
name: material
6+
features:
7+
- navigation.footer
8+
- palette.toggle
9+
- content.code.copy
10+
palette:
11+
# Palette toggle for automatic mode
12+
- media: "(prefers-color-scheme)"
13+
primary: pink
14+
toggle:
15+
icon: material/brightness-auto
16+
name: Switch to light mode
17+
18+
# Palette toggle for light mode
19+
- media: "(prefers-color-scheme: light)"
20+
scheme: default
21+
primary: pink
22+
toggle:
23+
icon: material/brightness-7
24+
name: Switch to dark mode
25+
26+
# Palette toggle for dark mode
27+
- media: "(prefers-color-scheme: dark)"
28+
scheme: slate
29+
primary: pink
30+
toggle:
31+
icon: material/brightness-4
32+
name: Switch to system preference
33+
markdown_extensions:
34+
- pymdownx.highlight
35+
- pymdownx.superfences
36+
nav:
37+
- Home: index.md
38+
- Installation: installation.md
39+
- Methods: methods.md
40+
- Dispatch: dispatch.md
41+
- Async: async.md
42+
- FAQ: faq.md
43+
- Examples: examples.md

0 commit comments

Comments
 (0)