commit ae57339500442ef58e300360b7a479ac7e2aa88d
parent 02f99af868d96f5f76edd952bfeb588cee0c8fb2
Author: nolash <dev@holbrook.no>
Date:   Fri,  9 Apr 2021 12:33:21 +0200
Add parser
Diffstat:
5 files changed, 221 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,2 +1,5 @@
 __pycache__
 *.pyc
+dist/
+*.egg-info
+gmon.out
diff --git a/jsonrpc_std/base.py b/jsonrpc_std/base.py
@@ -0,0 +1,4 @@
+class JSONRPCBase:
+    major_version = 2
+    minor_version = 0
+    version_string = '2.0'
diff --git a/jsonrpc_std/error.py b/jsonrpc_std/error.py
@@ -0,0 +1,86 @@
+# local imports
+from .base import JSONRPCBase
+
+class JSONRPCException(Exception, JSONRPCBase):
+    message = 'Unknown'
+
+    def __init__(self, v):
+       context_v = '{} error'.format(self.message)
+       if v != None:
+           context_v += ': ' + v
+
+       super(JSONRPCException, self).__init__(context_v)
+
+
+class JSONRPCCustomException(JSONRPCException):
+    code = -32000
+    message = 'Server'
+
+
+class JSONRPCParseError(JSONRPCException):
+    code = -32700
+    message = 'Parse'
+
+
+class JSONRPCInvalidRequestError(JSONRPCException):
+    code = -32600
+    message = 'Invalid request'
+
+
+class JSONRPCMethodNotFoundError(JSONRPCException):
+    code = -32601
+    message = 'Method not found'
+
+
+class JSONRPCInvalidParametersError(JSONRPCException):
+    code = -32602
+    message = 'Invalid parameters'
+
+
+class JSONRPCInternalError(JSONRPCException):
+    code = -32603
+    message = 'Internal'
+
+
+class JSONRPCUnhandledErrorException(KeyError):
+    pass
+
+
+class JSONRPCErrors:
+    reserved_max = -31999
+    reserved_min = -32768
+    local_max = -32000
+    local_min = -32099
+
+    translations = {
+        -32700: JSONRPCParseError,
+        -32600: JSONRPCInvalidRequestError,
+        -32601: JSONRPCMethodNotFoundError,
+        -32602: JSONRPCInvalidParametersError,
+        -32603: JSONRPCInternalError,
+            }
+
+    @classmethod
+    def add(self, code, exception_object):
+        if code < self.local_min or code > self.local_max:
+            raise ValueError('code must be in range <{},{}>'.format(self.local_min, self.local_max))
+        exc = self.translations.get(code)
+        if exc != None:
+            raise ValueError('code already registered with {}'.format(exc))
+
+        if not issubclass(exception_object, JSONRPCCustomException):
+            raise ValueError('exception object must be a subclass of jsonrpc_base.error.JSONRPCCustomException')
+
+        self.translations[code] = exception_object
+
+
+    @classmethod
+    def get(self, code, v=None):
+        e = self.translations.get(code)
+        if e == None:
+            raise JSONRPCUnhandledErrorException(code)
+        return e(v)
+
+
+class InvalidJSONRPCError(ValueError):
+    pass
diff --git a/jsonrpc_std/interface.py b/jsonrpc_std/interface.py
@@ -0,0 +1,74 @@
+# standard imports
+import uuid
+
+# local imports
+from .base import JSONRPCBase
+from .error import (
+        JSONRPCErrors,
+        )
+
+
+class DefaultErrorParser:
+
+    def translate(self, error):
+        code = error['error']['code']
+        message = error['error']['message']
+        if type(code).__name__ != 'int':
+            raise ValueError('error code is not int by {} in error {}'.format(type(code), error))
+        
+        exc = None
+        try:
+            exc = JSONRPCErrors.get(code, message)
+        except KeyError:
+            return JSONRPCUndefinedError(code, message)
+
+
+def jsonrpc_template(request_id=None):
+    if request_id == None:
+        request_id = str(uuid.uuid4())
+
+    return {
+        'jsonrpc': JSONRPCBase.version_string,
+        'id': request_id,
+        'method': None,
+        'params': [],
+            }
+
+
+def jsonrpc_request(method, request_id=None):
+    req = jsonrpc_template(request_id=request_id)
+    req['method'] = method
+    return req
+
+
+def jsonrpc_result(o, ep):
+    if o.get('error') != None:
+        raise ep.translate(o)
+    return o['result']
+
+
+def jsonrpc_response(request_id, result):
+    return {
+        'jsonrpc': JSONRPCBase.version_string,
+        'id': request_id,
+        'result': result,
+        }
+
+
+def jsonrpc_error(request_id, code, message=None):
+    e = JSONRPCErrors.get(code, message)
+    return {
+        'jsonrpc': JSONRPCBase.version_string,
+        'id': request_id,
+        'error': {
+            'code': code,
+            'message': str(e),
+            },
+        }
+
+
+def jsonrpc_is_response_to(request, response):
+    return request['id'] == response['id']
+
+
+
diff --git a/jsonrpc_std/parse.py b/jsonrpc_std/parse.py
@@ -0,0 +1,54 @@
+# standard imports
+import json
+
+# local imports
+from .base import JSONRPCBase
+from .error import (
+        JSONRPCParseError,
+        JSONRPCInvalidRequestError,
+        )
+from .interface import (
+        jsonrpc_request,
+        )
+
+
+def jsonrpc_validate_dict(o):
+    version = o.get('jsonrpc')
+    if version == None:
+        raise JSONRPCParseError('missing jsonrpc version field')
+    elif version != JSONRPCBase.version_string:
+        raise JSONRPCInvalidRequestError('Invalid version {}'.format(version))
+
+    method = o.get('method')
+    if method == None:
+        raise JSONRPCParseError('missing method field')
+
+    params = o.get('params')
+    if params == None:
+        raise JSONRPCParseError('missing params field')
+    elif type(params).__name__ != 'list':
+        raise JSONRPCParseError('params field must be array')
+
+    request_id = o.get('id')
+    if request_id == None:
+        raise JSONRPCParseError('missing id field')
+    if type(request_id).__name__ not in ['str', 'int']:
+        raise JSONRPCInvalidRequestError('invalid id value, must be string or integer')
+
+    return o 
+
+
+def jsonrpc_from_str(s):
+    o = json.loads(s)
+    return jsonrpc_from_dict(o)
+
+
+def jsonrpc_from_dict(o):
+    o = jsonrpc_validate_dict(o)
+    req = jsonrpc_request(o['method'], request_id=o['id'])
+    req['params'] = o['params']
+    return req
+
+def jsonrpc_from_file(f):
+    o = json.load(f)
+    return jsonrpc_from_dict(o)