Пример #1
0
    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
Пример #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
Пример #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
Пример #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)
    ]
Пример #5
0
def test_error_info():
    """test of ErrorInfo class 
    """

    err = ErrorInfo()
    assert err.code == 0
    assert err.message == ""
    assert err.data == MessageData()
    assert not err

    expected_str = "mprpc.ErrorInfo()"
    assert str(err) == expected_str
    # pylint: disable=eval-used
    assert str(eval(repr(err))) == expected_str

    code = 37
    message = 'test message'
    err = ErrorInfo(code, message)
    assert err.code == code
    assert err.message == message
    assert err.data == MessageData()
    assert err

    expected_str = "mprpc.ErrorInfo(code=37, message='test message')"
    assert str(err) == expected_str
    # pylint: disable=eval-used
    assert str(eval(repr(err))) == expected_str

    data = MessageData(b'\x93\x01\x02\x03')
    err = ErrorInfo(code, message, data)
    assert err.code == code
    assert err.message == message
    assert err.data == data
    assert err

    expected_str = "mprpc.ErrorInfo(code=37, message='test message', " + \
        "data=<mprpc.message.MessageData: [1,2,3]>)"
    assert str(err) == expected_str
    # pylint: disable=eval-used
    assert str(eval(repr(err))) == expected_str
Пример #6
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)))
Пример #7
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
Пример #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
Пример #9
0
        def done_callback(future: Future):
            """callback called after message is precessed

            Parameters
            ----------
            future : Future
                future
            """

            nonlocal handler

            error = ErrorInfo()
            response_data = None

            try:
                response_data = future.result()
            except MPRPCException as err:
                error = err.args[0]

            has_response = response_data is not None
            if not has_response:
                response_data = MessageData()

            handler(error, has_response, response_data)
Пример #10
0
 def server_process_message(
         _, data: MessageData,
         handler: Callable[[ErrorInfo, bool, MessageData], Any]):
     nonlocal request_received_event
     request_received_event.set()
     handler(ErrorInfo(), True, data)