This commit is contained in:
2025-06-21 02:26:54 +03:00
parent 8c9cd425c5
commit e1fbc591f8
6 changed files with 93 additions and 43 deletions

View File

@@ -1,6 +1,6 @@
[project]
name = "jsonrpc"
version = "1.0"
version = "2.0"
authors = [
{ name="RemiZOffAlex", email="remizoffalex@gmail.com" },
]

View File

@@ -1,7 +1,6 @@
__author__ = 'RemiZOffAlex'
__email__ = 'remizoffalex@mail.ru'
import json
import logging
import traceback
@@ -27,7 +26,7 @@ class Response:
class Method:
def __init__(self, function, middlewares = [], debug: bool = False):
def __init__(self, function, middlewares=[], debug: bool = False):
self.function = function
self.middlewares = middlewares
self.debug = debug
@@ -65,11 +64,9 @@ class Method:
response = middleware(query)
return response
response = self.exec_function(query)
return response
def __repr__(self):
return '<{}>'.format(self.function)
@@ -86,15 +83,15 @@ class JSONRPC:
self.methods = {}
self.debug = debug
def method(self, name: str, middlewares = None):
def method(self, name: str, middlewares=None):
"""Декоратор метода
"""
assert len(name) > 0, 'Не указано имя метода'
def wrap(function):
method = Method(
function = function,
middlewares = middlewares
function=function,
middlewares=middlewares
)
self.methods[name] = method
return function
@@ -151,31 +148,31 @@ class JSONRPC:
def validate(self, query):
"""Валидация запроса
"""
keys = query.keys()
if 'id' not in query:
return InvalidRequestError(
id=query['id'],
message=f'Некорректный запрос: {query}'
).__json__()
if 'method' not in query:
return InvalidRequestError(
id=query['id'],
result = InvalidRequestError(
message=f'Некорректный запрос: {query}'
).__json__()
)
if 'id' in query:
result.id = query['id']
return result
def process(self, query):
"""Выполнение метода
"""
self.validate(query)
result = self.validate(query)
if isinstance(result, JSONRPCError):
return result
name = query['method']
if name not in self.methods:
if 'id' in query:
__id = query['id']
else:
__id = None
result = MethodNotFoundError(
query['id'],
message=f'Метод не найден: {name}'
message=f'Метод не найден: {name}',
id=__id
)
return result.__json__()
return result
method = self.methods[name]
@@ -198,11 +195,14 @@ class JSONRPC:
# message=traceback.format_exc()
)
else:
response = Response(
id=query['id'],
result=response
)
result = response.__json__()
if 'id' in query:
response = Response(
id=query['id'],
result=response
)
else:
return
result = response
return result
def __call__(self, queries):
@@ -213,7 +213,9 @@ class JSONRPC:
elif isinstance(queries, list):
result = []
for query in queries:
result.append(self.process(query))
response = self.process(query)
if response:
result.append(response)
return result
def __getitem__(self, key):

View File

@@ -2,19 +2,30 @@ __author__ = 'RemiZOffAlex'
__email__ = 'remizoffalex@mail.ru'
import jinja2
import logging
import pathlib
import aiohttp_jinja2
from aiohttp.web import Response, json_response
from .. import JSONRPC
from ..exceptions import ParseError
log = logging.getLogger(__name__)
pathlib.Path(__file__).parent.resolve()
class APIHandler:
def __init__(self, jsonrpc):
def __init__(
self,
jsonrpc,
debug: bool = False
):
self.jsonrpc = jsonrpc
self.debug = debug
if self.debug:
log.debug('Connect JSON-RPC to aiohttp complete')
@aiohttp_jinja2.template('api_browse.html')
async def get(self, request) -> Response:
@@ -25,9 +36,16 @@ class APIHandler:
return pagedata
async def post(self, request) -> Response:
json_data = await request.json()
result = self.jsonrpc(json_data)
return json_response(result)
try:
json_data = await request.json()
result = self.jsonrpc(json_data)
return json_response(result)
except ValueError as e:
log.error('invalid json: %s', request_data)
log.exception(e)
raise ParseError(
message={'message': 'Invalid JSON: {0!r}'.format(request_data)}
)
def api_init(app, jsonrpc: JSONRPC, rule: str = '/api'):
@@ -38,6 +56,6 @@ def api_init(app, jsonrpc: JSONRPC, rule: str = '/api'):
pathlib.Path(__file__).parent.resolve() / 'templates'
)
)
handler = APIView(jsonrpc)
handler = APIHandler(jsonrpc)
app.router.add_route('GET', rule, handler.get)
app.router.add_route('POST', rule, handler.post)

View File

@@ -6,6 +6,7 @@ from flask.views import MethodView
from flask import jsonify, Response, request, render_template
from ..exceptions import ParseError
from ..serialize import JSONRPCEncoder
log = logging.getLogger(__name__)
@@ -20,9 +21,13 @@ def to_json(
try:
return json.loads(request_data)
except ValueError as e:
print('invalid json:', request_data)
log.error('invalid json: %s', request_data)
log.exception(e)
raise ParseError(data={'message': 'Invalid JSON: {0!r}'.format(request_data)})
result = ParseError(
message={'message': 'Invalid JSON: {0!r}'.format(request_data)}
)
return result
# raise ValueError('Invalid JSON')
@@ -43,8 +48,12 @@ class APIView(MethodView):
return body
def post(self):
if self.debug:
print('request.data', request.data)
json_data = to_json(request.data)
result = self.jsonrpc(json_data)
if self.debug:
print(result)
log.debug(result)
return jsonify(result)
# return jsonify(result)
return json.dumps(result, cls=JSONRPCEncoder)

View File

@@ -30,12 +30,14 @@ class Client:
self.url,
data=payload,
headers=self.headers
).json()
)
print('content', response.content)
result = response.json()
assert 'jsonrpc' in response
assert 'id' in response
assert response["jsonrpc"] in ['2.0', '3.0']
if '3.0' in response["jsonrpc"]:
assert 'meta' in response
assert 'jsonrpc' in result
assert 'id' in result
assert result["jsonrpc"] in ['2.0', '3.0']
if '3.0' in result["jsonrpc"]:
assert 'meta' in result
return response
return result

19
src/jsonrpc/serialize.py Normal file
View File

@@ -0,0 +1,19 @@
__author__ = 'RemiZOffAlex'
__email__ = 'remizoffalex@mail.ru'
import json
from datetime import datetime
from .exceptions import JSONRPCError
class JSONRPCEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
elif isinstance(obj, JSONRPCError):
return obj.__json__()
elif hasattr(obj, '__dict__'):
return obj.__dict__
return super().default(obj)