"""The public functions. These three public functions all perform the same function of dispatching a JSON-RPC request, but they each give a different return value. - dispatch_to_responses: Returns Response(s) (or None for notifications). - dispatch_to_serializable: Returns a Python dict or list of dicts (or None for notifications). - dispatch_to_json/dispatch: Returns a JSON-RPC response string (or an empty string for notifications). """ from typing import Any, Callable, Dict, List, Optional, Union, cast import json from jsonschema.validators import validator_for # type: ignore from pkg_resources import resource_string from .dispatcher import dispatch_to_response_pure, Deserialized from .methods import Methods, global_methods from .response import Response, to_serializable_one from .sentinels import NOCONTEXT from .utils import identity default_deserializer = json.loads # Prepare the jsonschema validator. This is global so it loads only once, not every # time dispatch is called. schema = json.loads(resource_string(__name__, "request-schema.json")) klass = validator_for(schema) klass.check_schema(schema) default_validator = klass(schema).validate def dispatch_to_response( request: str, methods: Optional[Methods] = None, *, context: Any = NOCONTEXT, deserializer: Callable[[str], Deserialized] = json.loads, validator: Callable[[Deserialized], Deserialized] = default_validator, post_process: Callable[[Response], Any] = identity, ) -> Union[Response, List[Response], None]: """Takes a JSON-RPC request string and dispatches it to method(s), giving Response namedtuple(s) or None. This is a public wrapper around dispatch_to_response_pure, adding globals and default values to be nicer for end users. Args: request: The JSON-RPC request string. methods: Dictionary of methods that can be called - mapping of function names to functions. If not passed, uses the internal global_methods dict which is populated with the @method decorator. context: If given, will be passed as the first argument to methods. deserializer: Function that deserializes the request string. validator: Function that validates the JSON-RPC request. The function should raise an exception if the request is invalid. To disable validation, pass lambda _: None. post_process: Function that will be applied to Responses. Returns: A Response, list of Responses or None. Examples: >>> dispatch('{"jsonrpc": "2.0", "method": "ping", "id": 1}') '{"jsonrpc": "2.0", "result": "pong", "id": 1}' """ return dispatch_to_response_pure( deserializer=deserializer, validator=validator, post_process=post_process, context=context, methods=global_methods if methods is None else methods, request=request, ) def dispatch_to_serializable( *args: Any, **kwargs: Any ) -> Union[Dict[str, Any], List[Dict[str, Any]], None]: """Takes a JSON-RPC request string and dispatches it to method(s), giving responses as dicts (or None). """ return cast( Union[Dict[str, Any], List[Dict[str, Any]], None], dispatch_to_response(*args, post_process=to_serializable_one, **kwargs), ) def dispatch_to_json( *args: Any, serializer: Callable[ [Union[Dict[str, Any], List[Dict[str, Any]], str]], str ] = json.dumps, **kwargs: Any, ) -> str: """Takes a JSON-RPC request string and dispatches it to method(s), giving a JSON-RPC response string. This is the main public method, it goes through the entire JSON-RPC process - it's a function from JSON-RPC request string to JSON-RPC response string. Args: serializer: A function to serialize a Python object to json. The rest: Passed through to dispatch_to_serializable. """ response = dispatch_to_serializable(*args, **kwargs) # Better to respond with the empty string instead of json "null", because "null" is # an invalid JSON-RPC response. return "" if response is None else serializer(response) # "dispatch" aliases dispatch_to_json. dispatch = dispatch_to_json