jsonrpcserver 5.1.0__tar.gz → 5.2.0__tar.gz
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.
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/CHANGELOG.md +18 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/PKG-INFO +1 -1
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/__init__.py +1 -1
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/async_dispatcher.py +2 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/dispatcher.py +13 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/main.py +39 -2
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/server.py +17 -5
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_batch_limit.py +3 -1
- jsonrpcserver-5.2.0/tests/test_future_warnings.py +81 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_robustness.py +4 -1
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_server.py +3 -2
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/CODE_OF_CONDUCT.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/CONTRIBUTING.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/LICENSE +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/README.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/RELEASING.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/SECURITY.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/assets/a11y.js +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/assets/apple-touch-icon.png +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/assets/copy-without-prompts.js +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/assets/extra.css +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/assets/favicon.png +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/assets/logo.png +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/assets/social-card.png +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/async.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/batches.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/changelog.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/context.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/contributing.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/dispatch.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/errors.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/aiohttp_server.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/check_examples.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/django_server.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/fastapi_server.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/flask_server.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/http_server.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/quickstart.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/sanic_server.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/socketio_server.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/tornado_server.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/websockets_server.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/werkzeug_server.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/zeromq_async_server.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples/zeromq_server.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/examples.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/faq.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/frameworks/aiohttp.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/frameworks/django.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/frameworks/fastapi.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/frameworks/flask.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/frameworks/http-server.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/frameworks/sanic.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/frameworks/socketio.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/frameworks/tornado.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/frameworks/websockets.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/frameworks/werkzeug.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/frameworks/zeromq.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/index.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/license.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/methods.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/migration.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/reference.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/roadmap.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/security-policy.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/security.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/testing.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/threads.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/typing.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/docs/validation.md +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/async_main.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/codes.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/exceptions.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/methods.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/py.typed +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/request-schema.json +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/request.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/response.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/result.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/sentinels.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/jsonrpcserver/utils.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/mkdocs.yml +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/overrides/404.html +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/overrides/main.html +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/pyproject.toml +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/requirements/build.txt +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/requirements/docs-constraints.txt +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/requirements/docs.txt +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/requirements/examples.txt +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/requirements/lint.txt +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/requirements/site.txt +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/requirements/test.txt +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/__init__.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/doc_examples.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_async_dispatcher.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_async_main.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_dispatcher.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_docs.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_docstrings.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_main.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_methods.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_request.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_response.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_result.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_sentinels.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_threading.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_typing.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_version.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tests/test_warnings.py +0 -0
- {jsonrpcserver-5.1.0 → jsonrpcserver-5.2.0}/tox.ini +0 -0
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.2.0 (2026-11-06)
|
|
4
|
+
|
|
5
|
+
Warnings about what 6.0 changes, so an upgrade doesn't surprise you. Nothing
|
|
6
|
+
else changes. Each is a `FutureWarning`, which Python shows by default.
|
|
7
|
+
|
|
8
|
+
- `serve()` without a host warns. From 6.0 it listens on localhost only.
|
|
9
|
+
`serve("", port)` keeps every network interface, quietly.
|
|
10
|
+
- A request with `NaN`, `Infinity`, `-Infinity` or a number too big for a float
|
|
11
|
+
warns. 6.0 rejects them with a Parse error. `deserializer=json.loads` accepts
|
|
12
|
+
them quietly. `dispatch_to_response` now uses the same default deserializer
|
|
13
|
+
as the other dispatch functions.
|
|
14
|
+
- A batch of more than 100 requests without `max_batch_size` warns. 6.0 limits
|
|
15
|
+
batches to 100 by default. Passing `max_batch_size` turns the warning off.
|
|
16
|
+
|
|
17
|
+
The [6.0 plan](https://github.com/bensynapse/jsonrpcserver/blob/main/plans/6.0.md)
|
|
18
|
+
lists everything else that changes, such as results and responses without
|
|
19
|
+
oslash.
|
|
20
|
+
|
|
3
21
|
## 5.1.0 (2026-11-06)
|
|
4
22
|
|
|
5
23
|
### Added
|
|
@@ -28,6 +28,7 @@ from .dispatcher import (
|
|
|
28
28
|
to_result,
|
|
29
29
|
validate_args,
|
|
30
30
|
validate_request,
|
|
31
|
+
warn_if_over_6_0_batch_limit,
|
|
31
32
|
)
|
|
32
33
|
from .exceptions import JsonRpcError
|
|
33
34
|
from .methods import AnyMethod, MethodsArgument
|
|
@@ -161,6 +162,7 @@ async def dispatch_to_response_pure(
|
|
|
161
162
|
return post_process(Left(parsed._error))
|
|
162
163
|
deserialized = unwrap(parsed)
|
|
163
164
|
if isinstance(deserialized, list) and deserialized:
|
|
165
|
+
warn_if_over_6_0_batch_limit(len(deserialized), max_batch_size)
|
|
164
166
|
if max_batch_size is not None and len(deserialized) > max_batch_size:
|
|
165
167
|
return post_process(
|
|
166
168
|
Left(BatchTooLargeResponse(len(deserialized), max_batch_size))
|
|
@@ -4,6 +4,7 @@ requests, providing responses.
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
import reprlib
|
|
7
|
+
import warnings
|
|
7
8
|
from functools import partial
|
|
8
9
|
from inspect import iscoroutine, signature
|
|
9
10
|
from itertools import starmap
|
|
@@ -387,6 +388,17 @@ def check_max_batch_size(max_batch_size: object) -> None:
|
|
|
387
388
|
)
|
|
388
389
|
|
|
389
390
|
|
|
391
|
+
def warn_if_over_6_0_batch_limit(size: int, max_batch_size: Optional[int]) -> None:
|
|
392
|
+
"""Warn when a batch would get an error under 6.0's default limit of 100."""
|
|
393
|
+
if max_batch_size is None and size > 100:
|
|
394
|
+
warnings.warn(
|
|
395
|
+
f"This batch has {size} requests. 6.0 limits batches to 100 by default. "
|
|
396
|
+
"Pass max_batch_size=None to keep no limit, or set the limit you want.",
|
|
397
|
+
FutureWarning,
|
|
398
|
+
stacklevel=2,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
|
|
390
402
|
def member_id(member: Any) -> Any:
|
|
391
403
|
"""The id of a batch member, or None if it has none we can use.
|
|
392
404
|
|
|
@@ -453,6 +465,7 @@ def dispatch_to_response_pure(
|
|
|
453
465
|
return post_process(Left(parsed._error))
|
|
454
466
|
deserialized = unwrap(parsed)
|
|
455
467
|
if isinstance(deserialized, list) and deserialized:
|
|
468
|
+
warn_if_over_6_0_batch_limit(len(deserialized), max_batch_size)
|
|
456
469
|
if max_batch_size is not None and len(deserialized) > max_batch_size:
|
|
457
470
|
return post_process(
|
|
458
471
|
Left(BatchTooLargeResponse(len(deserialized), max_batch_size))
|
|
@@ -12,7 +12,9 @@ They differ only in the form of the response:
|
|
|
12
12
|
|
|
13
13
|
import json
|
|
14
14
|
import logging
|
|
15
|
+
import math
|
|
15
16
|
import pkgutil
|
|
17
|
+
import warnings
|
|
16
18
|
from typing import Any, Callable, Dict, List, Optional, Union, cast
|
|
17
19
|
|
|
18
20
|
from jsonschema.validators import validator_for
|
|
@@ -41,7 +43,42 @@ from .utils import identity
|
|
|
41
43
|
|
|
42
44
|
logger = logging.getLogger(__name__)
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
_CONSTANTS = {"NaN": math.nan, "Infinity": math.inf, "-Infinity": -math.inf}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _future_constant(constant: str) -> float:
|
|
50
|
+
warnings.warn(
|
|
51
|
+
f"{constant} in a request isn't valid JSON. 5.x accepts it, but 6.0 will "
|
|
52
|
+
"reject it with a Parse error.",
|
|
53
|
+
FutureWarning,
|
|
54
|
+
stacklevel=2,
|
|
55
|
+
)
|
|
56
|
+
return _CONSTANTS[constant]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _future_float(text: str) -> float:
|
|
60
|
+
number = float(text)
|
|
61
|
+
if math.isinf(number):
|
|
62
|
+
warnings.warn(
|
|
63
|
+
f"{text} in a request is too big for a float. 5.x reads it as infinity, "
|
|
64
|
+
"but 6.0 will reject it with a Parse error.",
|
|
65
|
+
FutureWarning,
|
|
66
|
+
stacklevel=2,
|
|
67
|
+
)
|
|
68
|
+
return number
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def default_deserializer(request: str) -> Any:
|
|
72
|
+
"""json.loads, which warns about input 6.0 will reject.
|
|
73
|
+
|
|
74
|
+
`NaN`, `Infinity`, `-Infinity` and numbers too big for a float are accepted, as
|
|
75
|
+
in every 5.x release, with a `FutureWarning`: 6.0's default rejects them with a
|
|
76
|
+
Parse error. Pass `deserializer=json.loads` to accept them without a warning.
|
|
77
|
+
New in 5.2.0.
|
|
78
|
+
"""
|
|
79
|
+
return json.loads(
|
|
80
|
+
request, parse_constant=_future_constant, parse_float=_future_float
|
|
81
|
+
)
|
|
45
82
|
|
|
46
83
|
|
|
47
84
|
def default_serializer(response: Any) -> str:
|
|
@@ -121,7 +158,7 @@ def dispatch_to_response(
|
|
|
121
158
|
methods: Optional[MethodsArgument] = None,
|
|
122
159
|
*,
|
|
123
160
|
context: Any = NOCONTEXT,
|
|
124
|
-
deserializer: Callable[[str], Deserialized] =
|
|
161
|
+
deserializer: Callable[[str], Deserialized] = default_deserializer,
|
|
125
162
|
validator: Callable[[Deserialized], object] = default_validator,
|
|
126
163
|
post_process: Callable[[Response], Any] = identity,
|
|
127
164
|
debug: bool = False,
|
|
@@ -7,8 +7,9 @@ server or framework.
|
|
|
7
7
|
import json
|
|
8
8
|
import logging
|
|
9
9
|
import sys
|
|
10
|
+
import warnings
|
|
10
11
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
11
|
-
from typing import Any
|
|
12
|
+
from typing import Any, Optional
|
|
12
13
|
|
|
13
14
|
from .main import dispatch
|
|
14
15
|
from .response import ParseErrorResponse, to_error_dict
|
|
@@ -71,7 +72,7 @@ def listening_message(name: str, port: int) -> str:
|
|
|
71
72
|
return f" * Listening on http://{name}:{port}/. This is a development server."
|
|
72
73
|
|
|
73
74
|
|
|
74
|
-
def serve(name: str =
|
|
75
|
+
def serve(name: Optional[str] = None, port: int = 5000) -> None:
|
|
75
76
|
"""Serve the methods registered with `@method` over HTTP. For development only.
|
|
76
77
|
|
|
77
78
|
It answers POST requests on any path with `dispatch`, sends 204 No Content for a
|
|
@@ -87,9 +88,11 @@ def serve(name: str = "", port: int = 5000) -> None:
|
|
|
87
88
|
production.
|
|
88
89
|
|
|
89
90
|
Args:
|
|
90
|
-
name: The host name or address to listen on.
|
|
91
|
-
every network interface, so other machines can connect
|
|
92
|
-
|
|
91
|
+
name: The host name or address to listen on. Without one, it listens on
|
|
92
|
+
every network interface, so other machines can connect, and gives a
|
|
93
|
+
`FutureWarning` (5.2.0): 6.0 listens on localhost by default. Pass ""
|
|
94
|
+
to keep every interface, or "localhost" to accept only local
|
|
95
|
+
connections.
|
|
93
96
|
port: The port to listen on.
|
|
94
97
|
|
|
95
98
|
Example:
|
|
@@ -103,6 +106,15 @@ def serve(name: str = "", port: int = 5000) -> None:
|
|
|
103
106
|
serve("localhost", 8000)
|
|
104
107
|
```
|
|
105
108
|
"""
|
|
109
|
+
if name is None:
|
|
110
|
+
warnings.warn(
|
|
111
|
+
"serve() without a host listens on every network interface. From 6.0 it "
|
|
112
|
+
'will listen on localhost only. Pass serve("", port) to keep listening '
|
|
113
|
+
'on every interface, or serve("localhost", port).',
|
|
114
|
+
FutureWarning,
|
|
115
|
+
stacklevel=2,
|
|
116
|
+
)
|
|
117
|
+
name = ""
|
|
106
118
|
httpd = ThreadingHTTPServer((name, port), RequestHandler)
|
|
107
119
|
try:
|
|
108
120
|
message = listening_message(name, port)
|
|
@@ -47,7 +47,9 @@ def reset_calls() -> None:
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
def test_no_limit_by_default() -> None:
|
|
50
|
-
|
|
50
|
+
"""5.x has no limit. From 5.2.0 a batch over 6.0's default of 100 warns."""
|
|
51
|
+
with pytest.warns(FutureWarning, match="6.0 limits batches to 100 by default"):
|
|
52
|
+
assert len(json.loads(dispatch(batch(500), {"ping": ping}))) == 500
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
def test_at_the_limit() -> None:
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""5.2.0 warns about the 5.x behaviour that 6.0 changes (plans/6.0.md, D12)."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import warnings
|
|
5
|
+
from unittest.mock import Mock, patch
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from jsonrpcserver import Result, Success, dispatch, serve
|
|
10
|
+
|
|
11
|
+
METHODS = {"ping": lambda: Success("pong")}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def take_one(x: float) -> Result:
|
|
15
|
+
return Success()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def take_two(a: float, b: float) -> Result:
|
|
19
|
+
return Success()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def batch(size: int) -> str:
|
|
23
|
+
return json.dumps(
|
|
24
|
+
[{"jsonrpc": "2.0", "method": "ping", "id": n} for n in range(size)]
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.mark.parametrize("constant", ["NaN", "Infinity", "-Infinity"])
|
|
29
|
+
def test_non_json_constants_warn(constant: str) -> None:
|
|
30
|
+
request = (
|
|
31
|
+
'{"jsonrpc": "2.0", "method": "ping", "params": [' + constant + '], "id": 1}'
|
|
32
|
+
)
|
|
33
|
+
with pytest.warns(FutureWarning, match=f"{constant} in a request isn't valid JSON"):
|
|
34
|
+
dispatch(request, {"ping": take_one})
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_json_loads_accepts_them_quietly() -> None:
|
|
38
|
+
request = '{"jsonrpc": "2.0", "method": "ping", "params": [NaN], "id": 1}'
|
|
39
|
+
with warnings.catch_warnings():
|
|
40
|
+
warnings.simplefilter("error")
|
|
41
|
+
dispatch(request, {"ping": take_one}, deserializer=json.loads)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_ordinary_numbers_dont_warn() -> None:
|
|
45
|
+
request = '{"jsonrpc": "2.0", "method": "ping", "params": [1.5, 1e300], "id": 1}'
|
|
46
|
+
with warnings.catch_warnings():
|
|
47
|
+
warnings.simplefilter("error")
|
|
48
|
+
dispatch(request, {"ping": take_two})
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_batch_of_100_doesnt_warn() -> None:
|
|
52
|
+
with warnings.catch_warnings():
|
|
53
|
+
warnings.simplefilter("error")
|
|
54
|
+
dispatch(batch(100), METHODS)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_batch_of_101_warns() -> None:
|
|
58
|
+
with pytest.warns(FutureWarning, match="This batch has 101 requests"):
|
|
59
|
+
dispatch(batch(101), METHODS)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@pytest.mark.asyncio
|
|
63
|
+
async def test_async_batch_of_101_warns() -> None:
|
|
64
|
+
from jsonrpcserver import async_dispatch
|
|
65
|
+
|
|
66
|
+
with pytest.warns(FutureWarning, match="This batch has 101 requests"):
|
|
67
|
+
await async_dispatch(batch(101), METHODS)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_explicit_limit_doesnt_warn() -> None:
|
|
71
|
+
with warnings.catch_warnings():
|
|
72
|
+
warnings.simplefilter("error")
|
|
73
|
+
dispatch(batch(101), METHODS, max_batch_size=200)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@patch("jsonrpcserver.server.ThreadingHTTPServer")
|
|
77
|
+
def test_serve_with_a_host_doesnt_warn(server: Mock) -> None:
|
|
78
|
+
with warnings.catch_warnings():
|
|
79
|
+
warnings.simplefilter("error")
|
|
80
|
+
serve("", 5000)
|
|
81
|
+
serve("localhost", 5000)
|
|
@@ -134,7 +134,10 @@ def test_nan_allowed_with_plain_json_dumps() -> None:
|
|
|
134
134
|
|
|
135
135
|
|
|
136
136
|
def test_infinite_id_gets_null_id() -> None:
|
|
137
|
-
|
|
137
|
+
with pytest.warns(FutureWarning, match="too big for a float"):
|
|
138
|
+
response = dispatch(
|
|
139
|
+
'{"jsonrpc": "2.0", "method": "ping", "id": 1e400}', METHODS
|
|
140
|
+
)
|
|
138
141
|
assert json.loads(response) == {
|
|
139
142
|
"jsonrpc": "2.0",
|
|
140
143
|
"error": INTERNAL_ERROR,
|
|
@@ -18,7 +18,8 @@ from jsonrpcserver.server import RequestHandler, listening_message, serve
|
|
|
18
18
|
|
|
19
19
|
@patch("jsonrpcserver.server.ThreadingHTTPServer")
|
|
20
20
|
def test_serve(server: Mock) -> None:
|
|
21
|
-
|
|
21
|
+
with pytest.warns(FutureWarning, match="From 6.0 it will listen on localhost"):
|
|
22
|
+
serve()
|
|
22
23
|
server.assert_called_once_with(("", 5000), RequestHandler)
|
|
23
24
|
server.return_value.serve_forever.assert_called_once_with()
|
|
24
25
|
server.return_value.server_close.assert_called_once_with()
|
|
@@ -34,7 +35,7 @@ def test_serve_name_and_port(server: Mock) -> None:
|
|
|
34
35
|
def test_serve_closes_on_interrupt(server: Mock) -> None:
|
|
35
36
|
server.return_value.serve_forever.side_effect = KeyboardInterrupt
|
|
36
37
|
with pytest.raises(KeyboardInterrupt):
|
|
37
|
-
serve()
|
|
38
|
+
serve("", 5000)
|
|
38
39
|
server.return_value.server_close.assert_called_once_with()
|
|
39
40
|
|
|
40
41
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|