def process_message(self, session: Session,
                        data: MessageData) -> MessageData:
        """process a message

        Parameters
        ----------
        session : Session
            session
        data : MessageData
            message

        Returns
        -------
        MessageData
            response data (if exists).
        """

        try:
            msg = validate_message(unpack_object(data))

            if isinstance(msg, Request):
                if msg.method not in self.__methods:
                    self.__logger.error('method %s not found', msg.method)
                    return pack_response(msg.msgid,
                                         error='method {} not found'.format(
                                             msg.method))

                return self.__methods[msg.method].process_request(session, msg)

            if isinstance(msg, Notification):
                if msg.method not in self.__methods:
                    self.__logger.error('method %s not found', msg.method)
                    return None

                self.__methods[msg.method].process_notification(session, msg)
                return None

            raise MPRPCException(
                ErrorInfo(ErrorCode.INVALID_MESSAGE,
                          'message must be a request or response'))
        except MPRPCException as err:
            self.__logger.error('%s', err)
            raise
        except Exception as err:
            self.__logger.error('%s', err)
            raise MPRPCException(
                ErrorInfo(ErrorCode.UNEXPECTED_ERROR,
                          'error in server: {}'.format(err))) from err
Example #2
0
def unpack_object(data: MessageData) -> Any:
    """unpack data

    Parameters
    ----------
    data : mprpc.message.MessageData
        data to unpack

    Returns
    -------
    Any
        unpacked data

    Raises
    ------
    mprpc.MPRPCException
        If msgpack package failed to unpack data.
    """

    try:
        return unpackb(bytes(data))
    except Exception as err:
        raise MPRPCException(
            ErrorInfo(ErrorCode.PARSE_ERROR, 'error in unpacking data',
                      data)) from err
Example #3
0
def pack_object(obj: Any) -> MessageData:
    """pack an object

    This function calls msgpack.packb function.

    Parameters
    ----------
    obj : Any
        object to pack

    Returns
    -------
    mprpc.message.MessageData
        packed bytes

    Raises
    ------
    mprpc.MPRPCException
        If msgpack package failed to pack data.
    """

    try:
        return MessageData(packb(obj))
    except Exception as err:
        raise MPRPCException(
            ErrorInfo(ErrorCode.UNEXPECTED_ERROR,
                      'error in packing data: %s' % obj)) from err
Example #4
0
def validate_data_list(
        data: Any,
        schemas: Optional[List[Optional[marshmallow.Schema]]] = None
) -> List[Any]:
    """validate a list of data

    This function repeately calls :py:func:`mprpc.message.validate_data`
    to validate a list of data,
    used for validation of parameters in request and notification messages.

    Parameters
    ----------
    data : Any
        list of data to validate
    schemas : Optional[List[Optional[marshmallow.Schema]]], optional
        list of schemas, by default None to specify no validation.
        If a list is given, each element in the list is assumed to be None (no validation) or
        an object of a class inherited from marshmallow.Schema to use for validation.

    Returns
    -------
    List[Any]
        validated data

    Raises
    ------
    mprpc.MPRPCException
        If data is invalid.
    """

    if schemas is None:
        # no validation is needed
        return data

    if not isinstance(data, list):
        raise MPRPCException(
            ErrorInfo(ErrorCode.PARSE_ERROR, 'data is not a list: %s' % data))

    if len(schemas) != len(data):
        raise MPRPCException(
            ErrorInfo(ErrorCode.PARSE_ERROR,
                      'invalid length of data: %s' % data))

    return [
        validate_data(data_elem, schema)
        for (data_elem, schema) in zip(data, schemas)
    ]
Example #5
0
    def _process_message(self, err: ErrorInfo, data: MessageData):
        """process a message

        Parameters
        ----------
        err : ErrorInfo
            error
        data : MessageData
            message data
        """

        try:
            if err:
                raise MPRPCException(err)

            msg = validate_message(unpack_object(data))
            if not isinstance(msg, Response):
                raise MPRPCException(
                    ErrorInfo(ErrorCode.INVALID_MESSAGE,
                              'received a message which is not a response'))

            msgid = msg.msgid
            with self.__lock:
                if msgid not in self.__requests:
                    self.__logger.warn(
                        'invalid msgid in received response: %d', msgid)
                future = self.__requests[msgid]
                del self.__requests[msgid]

            if msg.error:
                future.set_exception(ServerError(msg.error))
                return

            future.set_result(msg.result)

        except MPRPCException as exc:
            self.__logger.error('%s', exc)
            with self.__lock:
                for future in self.__requests.values():
                    future.set_exception(MPRPCException(err))
                self.__requests = dict()
            return
Example #6
0
    def _on_request_sent(self, err: ErrorInfo):
        """process on a request sent

        Parameters
        ----------
        err : ErrorInfo
            error
        """

        if not err:
            return

        self.__logger.error('%s', err)
        with self.__lock:
            for future in self.__requests.values():
                future.set_exception(MPRPCException(err))
            self.__requests = dict()
Example #7
0
def _make_parse_error(message: str, data: Any):
    """raise an exception for a parse error

    Parameters
    ----------
    message : str
        error message
    data : Any
        data

    Returns
    -------
    MPRPCException
        exception object to raise
    """

    return MPRPCException(
        ErrorInfo(ErrorCode.PARSE_ERROR, '%s: %s' % (message, data)))
Example #8
0
def validate_data(data: Any,
                  schema: Optional[marshmallow.Schema] = None) -> Any:
    """validate data

    This function validate `data` with `schema` using `schema.load` function,
    and returns the result.

    Parameters
    ----------
    data : Any
        data to validate
    schema : Optional[Schema], optional
        schema of data, by default None to specify no validation.
        If an object other than None is provided,
        it is assumed to be an object of a class inherited from marshmallow.Schema.

    Returns
    -------
    Any
        validated data,
        `data` itself or an object of user-defined types
        (when `@post_load` decorator is used).

    Raises
    ------
    mprpc.MPRPCException
        If data is invalid.
    """

    if schema is None:
        # no validation is needed
        return data

    try:
        return schema.load(data)
    except marshmallow.ValidationError as err:
        raise MPRPCException(
            ErrorInfo(ErrorCode.PARSE_ERROR,
                      'failed to parse data: %s' % data)) from err