diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 2c22310..abc349c 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -11,6 +11,6 @@ jobs: python-version: 3.8 - run: pip install --upgrade pip - run: pip install types-setuptools "black<23" ruff "mypy<2" "jsonschema<5" pytest "returns<1" "aiohttp<4" "aiozmq<1" "django<5" "fastapi<1" "flask<3" "flask-socketio<5.3.1" "pyzmq" "sanic" "tornado<7" "uvicorn<1" "websockets<11" - - run: black --diff --check $(git ls-files -- '*.py' ':!:docs/*') - - run: ruff $(git ls-files -- '*.py' ':!:docs/*') + - run: ruff check --select I $(git ls-files -- '*.py' ':!:docs/*') + - run: ruff format --check $(git ls-files -- '*.py' ':!:docs/*') - run: mypy --strict $(git ls-files -- '*.py' ':!:docs/*') diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a71d9a4..f884896 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,16 +3,16 @@ default_language_version: exclude: (^docs) fail_fast: true repos: - - repo: https://github.com/ambv/black - rev: 22.8.0 - hooks: - - id: black - args: [--diff, --check] - - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.265 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.1 hooks: - id: ruff + name: lint with ruff + - id: ruff + name: sort imports with ruff + args: [--select, I, --fix] + - id: ruff-format + name: format with ruff - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.2.0 diff --git a/docs/conf.py b/docs/conf.py index f876374..218ef00 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,6 +3,7 @@ # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html +from typing import List # -- Path setup -------------------------------------------------------------- @@ -38,7 +39,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [] +exclude_patterns: List[str] = [] # -- Options for HTML output ------------------------------------------------- diff --git a/examples/aiohttp_server.py b/examples/aiohttp_server.py index 22523bf..6626a32 100644 --- a/examples/aiohttp_server.py +++ b/examples/aiohttp_server.py @@ -1,6 +1,8 @@ """AioHTTP server""" + from aiohttp import web -from jsonrpcserver import async_dispatch, async_method, Ok, Result + +from jsonrpcserver import Ok, Result, async_dispatch, async_method @async_method diff --git a/examples/aiozmq_server.py b/examples/aiozmq_server.py index dff855a..545bceb 100644 --- a/examples/aiozmq_server.py +++ b/examples/aiozmq_server.py @@ -1,9 +1,11 @@ """AioZMQ server""" + import asyncio import aiozmq # type: ignore import zmq -from jsonrpcserver import async_dispatch, async_method, Ok, Result + +from jsonrpcserver import Ok, Result, async_dispatch, async_method @async_method diff --git a/examples/asyncio_server.py b/examples/asyncio_server.py index d83b6cd..4352403 100644 --- a/examples/asyncio_server.py +++ b/examples/asyncio_server.py @@ -1,8 +1,9 @@ """Demonstrates processing a batch of 100 requests asynchronously with asyncio.""" + import asyncio import json -from jsonrpcserver import async_dispatch, async_method, Ok, Result +from jsonrpcserver import Ok, Result, async_dispatch, async_method @async_method diff --git a/examples/django_server.py b/examples/django_server.py index 5a80362..86f77fd 100644 --- a/examples/django_server.py +++ b/examples/django_server.py @@ -1,7 +1,9 @@ """Django server""" + from django.http import HttpRequest, HttpResponse # type: ignore from django.views.decorators.csrf import csrf_exempt # type: ignore -from jsonrpcserver import dispatch, method, Ok, Result + +from jsonrpcserver import Ok, Result, dispatch, method @method diff --git a/examples/fastapi_server.py b/examples/fastapi_server.py index c02b216..858ba07 100644 --- a/examples/fastapi_server.py +++ b/examples/fastapi_server.py @@ -1,7 +1,9 @@ """FastAPI server""" -from fastapi import FastAPI, Request, Response + import uvicorn -from jsonrpcserver import dispatch, method, Ok, Result +from fastapi import FastAPI, Request, Response + +from jsonrpcserver import Ok, Result, dispatch, method app = FastAPI() diff --git a/examples/flask_server.py b/examples/flask_server.py index 32044b0..071b110 100644 --- a/examples/flask_server.py +++ b/examples/flask_server.py @@ -1,6 +1,8 @@ """Flask server""" + from flask import Flask, Response, request -from jsonrpcserver import dispatch, method, Ok, Result + +from jsonrpcserver import Ok, Result, dispatch, method app = Flask(__name__) diff --git a/examples/http_server.py b/examples/http_server.py index 047cf6b..5b81a2a 100644 --- a/examples/http_server.py +++ b/examples/http_server.py @@ -2,9 +2,10 @@ Demonstrates using Python's builtin http.server module to serve JSON-RPC. """ + from http.server import BaseHTTPRequestHandler, HTTPServer -from jsonrpcserver import dispatch, method, Ok, Result +from jsonrpcserver import Ok, Result, dispatch, method @method diff --git a/examples/jsonrpcserver_server.py b/examples/jsonrpcserver_server.py index c0471bb..55f7db6 100644 --- a/examples/jsonrpcserver_server.py +++ b/examples/jsonrpcserver_server.py @@ -2,7 +2,8 @@ Uses jsonrpcserver's built-in "serve" function. """ -from jsonrpcserver import method, serve, Ok, Result + +from jsonrpcserver import Ok, Result, method, serve @method diff --git a/examples/sanic_server.py b/examples/sanic_server.py index 180d900..be80c26 100644 --- a/examples/sanic_server.py +++ b/examples/sanic_server.py @@ -1,8 +1,10 @@ """Sanic server""" + from sanic import Sanic from sanic.request import Request from sanic.response import HTTPResponse, json -from jsonrpcserver import dispatch_to_serializable, method, Ok, Result + +from jsonrpcserver import Ok, Result, dispatch_to_serializable, method app = Sanic("JSON-RPC app") diff --git a/examples/socketio_server.py b/examples/socketio_server.py index 1a5c2c7..7dd4b94 100644 --- a/examples/socketio_server.py +++ b/examples/socketio_server.py @@ -1,7 +1,9 @@ """SocketIO server""" + from flask import Flask, Request from flask_socketio import SocketIO, send # type: ignore -from jsonrpcserver import dispatch, method, Ok, Result + +from jsonrpcserver import Ok, Result, dispatch, method app = Flask(__name__) socketio = SocketIO(app) diff --git a/examples/tornado_server.py b/examples/tornado_server.py index b31473f..3a027a4 100644 --- a/examples/tornado_server.py +++ b/examples/tornado_server.py @@ -1,8 +1,10 @@ """Tornado server""" + from typing import Awaitable, Optional from tornado import ioloop, web -from jsonrpcserver import async_dispatch, async_method, Ok, Result + +from jsonrpcserver import Ok, Result, async_dispatch, async_method @async_method diff --git a/examples/websockets_server.py b/examples/websockets_server.py index 33098e4..ed8812e 100644 --- a/examples/websockets_server.py +++ b/examples/websockets_server.py @@ -1,8 +1,10 @@ """Websockets server""" + import asyncio from websockets.server import WebSocketServerProtocol, serve -from jsonrpcserver import async_dispatch, async_method, Ok, Result + +from jsonrpcserver import Ok, Result, async_dispatch, async_method @async_method diff --git a/examples/werkzeug_server.py b/examples/werkzeug_server.py index f2c242b..dcc8590 100644 --- a/examples/werkzeug_server.py +++ b/examples/werkzeug_server.py @@ -1,7 +1,9 @@ """Werkzeug server""" + from werkzeug.serving import run_simple from werkzeug.wrappers import Request, Response -from jsonrpcserver import method, Result, Ok, dispatch + +from jsonrpcserver import Ok, Result, dispatch, method @method diff --git a/examples/zeromq_server.py b/examples/zeromq_server.py index 328667a..d88368c 100644 --- a/examples/zeromq_server.py +++ b/examples/zeromq_server.py @@ -1,6 +1,8 @@ """ZeroMQ server""" + import zmq -from jsonrpcserver import dispatch, method, Ok, Result + +from jsonrpcserver import Ok, Result, dispatch, method socket = zmq.Context().socket(zmq.REP) diff --git a/jsonrpcserver/__init__.py b/jsonrpcserver/__init__.py index 69c66d5..8136ba0 100644 --- a/jsonrpcserver/__init__.py +++ b/jsonrpcserver/__init__.py @@ -1,9 +1,14 @@ """Jsonrpcserver""" + from returns.result import Result as R from .async_main import ( dispatch as async_dispatch, +) +from .async_main import ( dispatch_to_response as async_dispatch_to_response, +) +from .async_main import ( dispatch_to_serializable as async_dispatch_to_serializable, ) from .async_methods import method as async_method diff --git a/jsonrpcserver/async_dispatcher.py b/jsonrpcserver/async_dispatcher.py index 0c5e518..49ba167 100644 --- a/jsonrpcserver/async_dispatcher.py +++ b/jsonrpcserver/async_dispatcher.py @@ -1,13 +1,15 @@ """Async version of dispatcher.py""" + +import asyncio +import logging from functools import partial from inspect import signature from itertools import starmap from typing import Any, Callable, Iterable, Tuple, Union -import asyncio -import logging from returns.result import Failure, Result, Success +from .async_methods import Method, Methods from .dispatcher import ( Deserialized, create_request, @@ -21,8 +23,8 @@ validate_result, ) from .exceptions import JsonRpcError -from .async_methods import Method, Methods from .request import Request +from .response import Response, ServerErrorResponse from .result import ( ErrorResult, InternalErrorResult, @@ -30,7 +32,6 @@ MethodNotFoundResult, SuccessResult, ) -from .response import Response, ServerErrorResponse from .utils import make_list logger = logging.getLogger(__name__) diff --git a/jsonrpcserver/async_main.py b/jsonrpcserver/async_main.py index 39c0f35..d34801c 100644 --- a/jsonrpcserver/async_main.py +++ b/jsonrpcserver/async_main.py @@ -1,16 +1,16 @@ """Async version of main.py. The public async functions.""" + import json from typing import Any, Callable, Dict, Iterable, List, Optional, Union, cast from .async_dispatcher import dispatch_to_response_pure from .async_methods import Methods, global_methods from .dispatcher import Deserialized -from .main import default_jsonrpc_validator, default_deserializer +from .main import default_deserializer, default_jsonrpc_validator from .response import Response, to_serializable from .sentinels import NOCONTEXT from .utils import identity - # pylint: disable=missing-function-docstring,duplicate-code diff --git a/jsonrpcserver/async_methods.py b/jsonrpcserver/async_methods.py index 6de95a4..63edfc1 100644 --- a/jsonrpcserver/async_methods.py +++ b/jsonrpcserver/async_methods.py @@ -1,4 +1,5 @@ """Async methods""" + from typing import Any, Awaitable, Callable, Dict, Optional, cast from returns.result import Result diff --git a/jsonrpcserver/dispatcher.py b/jsonrpcserver/dispatcher.py index 060d434..2893012 100644 --- a/jsonrpcserver/dispatcher.py +++ b/jsonrpcserver/dispatcher.py @@ -1,23 +1,24 @@ """Dispatcher - does the hard work of this library: parses, validates and dispatches requests, providing responses. """ + # pylint: disable=protected-access +import logging from functools import partial from inspect import signature from itertools import starmap from typing import Any, Callable, Dict, Iterable, List, Tuple, Union -import logging -from returns.result import Result, Failure, Success +from returns.result import Failure, Result, Success from .exceptions import JsonRpcError from .methods import Method, Methods from .request import Request from .response import ( - Response, ErrorResponse, InvalidRequestResponse, ParseErrorResponse, + Response, ServerErrorResponse, SuccessResponse, ) diff --git a/jsonrpcserver/exceptions.py b/jsonrpcserver/exceptions.py index b2e2afb..0314e44 100644 --- a/jsonrpcserver/exceptions.py +++ b/jsonrpcserver/exceptions.py @@ -1,5 +1,7 @@ """Exceptions""" + from typing import Any + from .sentinels import NODATA diff --git a/jsonrpcserver/main.py b/jsonrpcserver/main.py index 66ba720..19070b2 100644 --- a/jsonrpcserver/main.py +++ b/jsonrpcserver/main.py @@ -9,9 +9,10 @@ - dispatch_to_json/dispatch: Returns a JSON-RPC response string (or an empty string for notifications). """ + +import json from importlib.resources import read_text from typing import Any, Callable, Dict, List, Union, cast -import json from jsonschema.validators import validator_for # type: ignore @@ -26,7 +27,6 @@ from .sentinels import NOCONTEXT from .utils import identity - default_args_validator = validate_args default_deserializer = json.loads diff --git a/jsonrpcserver/methods.py b/jsonrpcserver/methods.py index 81b0a36..c946a58 100644 --- a/jsonrpcserver/methods.py +++ b/jsonrpcserver/methods.py @@ -11,6 +11,7 @@ Methods can take either positional or named arguments, but not both. This is a limitation of JSON-RPC. """ + from typing import Any, Callable, Dict, Optional, cast from returns.result import Result diff --git a/jsonrpcserver/request.py b/jsonrpcserver/request.py index 37f88c1..d82043e 100644 --- a/jsonrpcserver/request.py +++ b/jsonrpcserver/request.py @@ -3,6 +3,7 @@ After parsing a request string, we put the (dict) requests into these Request namedtuples, simply because they're nicer to work with. """ + from typing import Any, Dict, List, NamedTuple, Union diff --git a/jsonrpcserver/response.py b/jsonrpcserver/response.py index 9c6e4f1..3113d05 100644 --- a/jsonrpcserver/response.py +++ b/jsonrpcserver/response.py @@ -2,9 +2,10 @@ https://www.jsonrpc.org/specification#response_object """ + from typing import Any, Dict, List, NamedTuple, Union -from returns.result import Result, Failure +from returns.result import Failure, Result from .codes import ( ERROR_INVALID_REQUEST, @@ -101,7 +102,7 @@ def to_dict(response: Response) -> Dict[str, Any]: def to_serializable( - response: Union[Response, List[Response], None] + response: Union[Response, List[Response], None], ) -> Union[Deserialized, None]: """Serialize a response object (or list of them), to a dict, or list of them.""" if response is None: diff --git a/jsonrpcserver/result.py b/jsonrpcserver/result.py index a35315f..b193880 100644 --- a/jsonrpcserver/result.py +++ b/jsonrpcserver/result.py @@ -6,11 +6,13 @@ The public functions are Success, Error and InvalidParams. """ + from typing import Any, NamedTuple -from returns.result import Failure, Result as R, Success +from returns.result import Failure, Success +from returns.result import Result as R -from .codes import ERROR_INVALID_PARAMS, ERROR_METHOD_NOT_FOUND, ERROR_INTERNAL_ERROR +from .codes import ERROR_INTERNAL_ERROR, ERROR_INVALID_PARAMS, ERROR_METHOD_NOT_FOUND from .sentinels import NODATA # pylint: disable=missing-class-docstring,missing-function-docstring,invalid-name diff --git a/jsonrpcserver/server.py b/jsonrpcserver/server.py index ec0ddb8..e783082 100644 --- a/jsonrpcserver/server.py +++ b/jsonrpcserver/server.py @@ -1,6 +1,7 @@ """A simple development server for serving JSON-RPC requests using Python's builtin http.server module. """ + import logging from http.server import BaseHTTPRequestHandler, HTTPServer diff --git a/jsonrpcserver/utils.py b/jsonrpcserver/utils.py index 4ee5a2c..a259799 100644 --- a/jsonrpcserver/utils.py +++ b/jsonrpcserver/utils.py @@ -1,4 +1,5 @@ """Utility functions""" + from functools import reduce from typing import Any, Callable, List diff --git a/pyproject.toml b/pyproject.toml index 91b82cd..5e4171f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ examples = [ "flask-socketio", "gmqtt", "pyzmq", + "sanic", "tornado", "websockets", "werkzeug", diff --git a/tests/test_async_dispatcher.py b/tests/test_async_dispatcher.py index 4030ca3..b1afbfd 100644 --- a/tests/test_async_dispatcher.py +++ b/tests/test_async_dispatcher.py @@ -1,7 +1,8 @@ """Test async_dispatcher.py""" + from unittest.mock import Mock, patch -import pytest +import pytest from returns.result import Failure, Success from jsonrpcserver.async_dispatcher import ( @@ -10,9 +11,9 @@ dispatch_request, dispatch_to_response_pure, ) -from jsonrpcserver.main import default_deserializer, default_jsonrpc_validator from jsonrpcserver.codes import ERROR_INTERNAL_ERROR, ERROR_SERVER_ERROR from jsonrpcserver.exceptions import JsonRpcError +from jsonrpcserver.main import default_deserializer, default_jsonrpc_validator from jsonrpcserver.request import Request from jsonrpcserver.response import ErrorResponse, SuccessResponse from jsonrpcserver.result import ErrorResult, Ok, Result, SuccessResult diff --git a/tests/test_async_main.py b/tests/test_async_main.py index acfb56b..7686a8e 100644 --- a/tests/test_async_main.py +++ b/tests/test_async_main.py @@ -1,12 +1,12 @@ """Test async_main.py""" -import pytest +import pytest from returns.result import Success from jsonrpcserver.async_main import ( + dispatch_to_json, dispatch_to_response, dispatch_to_serializable, - dispatch_to_json, ) from jsonrpcserver.response import SuccessResponse from jsonrpcserver.result import Ok, Result diff --git a/tests/test_dispatcher.py b/tests/test_dispatcher.py index 014a8a7..26fe49a 100644 --- a/tests/test_dispatcher.py +++ b/tests/test_dispatcher.py @@ -2,11 +2,12 @@ TODO: Add tests for dispatch_requests (non-pure version) """ + +import json from typing import Any, Dict from unittest.mock import Mock, patch, sentinel -import json -import pytest +import pytest from returns.result import Failure, Success from jsonrpcserver.codes import ( @@ -23,9 +24,9 @@ dispatch_deserialized, dispatch_request, dispatch_to_response_pure, - extract_list, extract_args, extract_kwargs, + extract_list, get_method, not_notification, to_response, diff --git a/tests/test_main.py b/tests/test_main.py index de1e5ee..fae47ce 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,4 +1,5 @@ """Test main.py""" + from returns.result import Success from jsonrpcserver.main import ( diff --git a/tests/test_methods.py b/tests/test_methods.py index cd62ae3..4459e7a 100644 --- a/tests/test_methods.py +++ b/tests/test_methods.py @@ -1,6 +1,7 @@ """Test methods.py""" -from jsonrpcserver.result import Ok, Result + from jsonrpcserver.methods import global_methods, method +from jsonrpcserver.result import Ok, Result # pylint: disable=missing-function-docstring diff --git a/tests/test_request.py b/tests/test_request.py index 114a34d..67e803b 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -1,4 +1,5 @@ """Test request.py""" + from jsonrpcserver.request import Request # pylint: disable=missing-function-docstring diff --git a/tests/test_response.py b/tests/test_response.py index 5771ded..7829349 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -1,4 +1,5 @@ """Test response.py""" + from unittest.mock import sentinel from returns.result import Failure, Success diff --git a/tests/test_result.py b/tests/test_result.py index dc02117..797263c 100644 --- a/tests/test_result.py +++ b/tests/test_result.py @@ -1,13 +1,14 @@ """Test result.py""" + from unittest.mock import sentinel from returns.result import Failure, Success from jsonrpcserver.result import ( - Ok, Error, ErrorResult, InvalidParamsResult, + Ok, SuccessResult, ) from jsonrpcserver.sentinels import NODATA diff --git a/tests/test_sentinels.py b/tests/test_sentinels.py index c5c8f02..b1e5ffe 100644 --- a/tests/test_sentinels.py +++ b/tests/test_sentinels.py @@ -1,4 +1,5 @@ """Test sentinels.py""" + from jsonrpcserver.sentinels import Sentinel # pylint: disable=missing-function-docstring diff --git a/tests/test_server.py b/tests/test_server.py index ad37266..46c2b51 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -1,4 +1,5 @@ """Test server.py""" + from unittest.mock import Mock, patch from jsonrpcserver.server import serve