Пример #1
0
    def __init__(self, logger: Logger, config: ClientConfig):
        self.__impl = PythonClientHelper(logger, config, self._process_message)
        self.__logger = LabeledLogger(logger, 'mprpc.client')
        self.__sync_request_timeout_ms = config.sync_request_timeout_ms

        self.__lock = Lock()
        self.__requests = dict()
        self.__next_msgid = 0
Пример #2
0
def test_labeled_logger_from_labeled_logger_without_label():
    """test of LabeledLogger created from LabeledLogger object without label
    """

    origin = MockLogger()
    label = 'test_logger'
    logger = LabeledLogger(LabeledLogger(origin, label))

    assert logger.logger == origin
    assert logger.label == label

    logger.trace('test %s', 'log')
    assert origin.label == label
 def __init__(
         self,
         logger: Logger,
         name: str,
         function: Callable,
         *,
         param_schemas: Optional[List[Optional[marshmallow.Schema]]] = None,
         result_schema: Optional[marshmallow.Schema] = None):
     super().__init__(name)
     self.__function = function
     self.__param_schemas = param_schemas
     self.__result_schema = result_schema
     self.__logger = LabeledLogger(logger, 'mprpc.method.' + name)
Пример #4
0
def test_labeled_logger_from_labeled_logger_with_label():
    """test of LabeledLogger created from LabeledLogger object with label
    """

    origin = MockLogger()
    label1 = 'test_logger'
    label2 = 'test'
    logger = LabeledLogger(LabeledLogger(origin, label1), label2)

    label = label1 + '.' + label2

    assert logger.logger == origin
    assert logger.label == label

    logger.trace('test %s', 'log')
    assert origin.label == label
Пример #5
0
class Client:
    """class of clients

    logger : Logger
        logger
    config : ClientConfig
        client configuration
    """
    def __init__(self, logger: Logger, config: ClientConfig):
        self.__impl = PythonClientHelper(logger, config, self._process_message)
        self.__logger = LabeledLogger(logger, 'mprpc.client')
        self.__sync_request_timeout_ms = config.sync_request_timeout_ms

        self.__lock = Lock()
        self.__requests = dict()
        self.__next_msgid = 0

    def start(self):
        """start process
        """

        self.__impl.start()

    def stop(self):
        """stop process
        """

        self.__impl.stop()

    def async_request(self, method: str, *args) -> Future:
        """asynchronously request

        Parameters
        ----------
        method : str
            method, remaining arguments are parameters to the method

        Returns
        -------
        Future
            future
        """

        with self.__lock:
            msgid = self.__next_msgid
            data = pack_request(msgid, method, args)
            future = Future()
            self.__impl.async_send(data, self._on_request_sent)
            future.set_running_or_notify_cancel()
            self.__next_msgid += 1
            self.__requests[msgid] = future

        return future

    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()

    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

    def request(self, method: str, *args) -> Any:
        """request

        Parameters
        ----------
        method : str
            method, remaining arguments are parameters to the method

        Returns
        -------
        Any
            result
        """

        timeout = self.__sync_request_timeout_ms * 1e-3
        return self.async_request(method, *args).result(timeout=timeout)

    def notify(self, method: str, *args):
        """notify

        Parameters
        ----------
        method : str
            method, remaining arguments are parameters to the method
        """

        data = pack_notification(method, args)
        self.__impl.async_send(data, self._on_notification_sent)

    def _on_notification_sent(self, err: ErrorInfo):
        """process on notification sent

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

        if not err:
            return

        self.__logger.error('%s', err)
Пример #6
0
 def __init__(self, logger: Logger, methods: List[MethodExecutor]):
     self.__logger = LabeledLogger(logger, 'mprpc.simple_method_server')
     self.__methods = {method.name: method for method in methods}
     self.__threads = ThreadPoolExecutor(1)
Пример #7
0
class SimpleMethodServer(MethodServer):
    """class of a simple server implementation to execute methods

    Parameters
    ----------
    logger : Logger
        logger
    num_threads : int
        number of threads
    methods : List[MethodExecutor]
        list of methods
    """
    def __init__(self, logger: Logger, methods: List[MethodExecutor]):
        self.__logger = LabeledLogger(logger, 'mprpc.simple_method_server')
        self.__methods = {method.name: method for method in methods}
        self.__threads = ThreadPoolExecutor(1)

    def async_process_message(self, session: Session, msg: MessageData,
                              handler: Callable[[ErrorInfo, bool, MessageData],
                                                Any]):
        """asynchronously process message

        Parameters
        ----------
        session : Session
            session
        msg : MessageData
            message
        handler : Callable[[ErrorInfo, bool, MessageData], Any]
            handler on message processed
            Parameters are error, flag whether a response exists, and response data (if exists).
        """
        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)

        self.__threads.submit(self.process_message, session,
                              msg).add_done_callback(done_callback)

    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
Пример #8
0
def test_labeled_logger_from_logger_without_label():
    """test of LabeledLogger created from logger without label
    """

    origin = MockLogger()
    logger = LabeledLogger(origin)

    assert logger.logger == origin
    assert logger.label == 'mprpc'

    logger.trace('test %s', 'log')
    assert origin.label == 'mprpc'
    assert origin.filename == __file__
    assert origin.line == 45
    assert origin.function == 'test_labeled_logger_from_logger_without_label'
    assert origin.log_level == LogLevel.TRACE
    assert origin.message == 'test log'

    logger.debug('test %s', 'log')
    assert origin.log_level == LogLevel.DEBUG

    logger.info('test %s', 'log')
    assert origin.log_level == LogLevel.INFO

    logger.warn('test %s', 'log')
    assert origin.log_level == LogLevel.WARN

    logger.error('test %s', 'log')
    assert origin.log_level == LogLevel.ERROR

    logger.fatal('test %s', 'log')
    assert origin.log_level == LogLevel.FATAL
class FunctionMethodExecutor(MethodExecutor):
    """class of executors of methods written in functions

    Parameters
    ----------
    logger : Logger
        logger
    name : str
        method name
    function : Callable
        function to call for method execution
    param_schemas : Optional[List[Optional[marshmallow.Schema]]], optional
        list of schemas for parameters, 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.
    result_schema : Optional[marshmallow.Schema]
        schema for result of method, by default None to specify no serialization.
        If a schema is given, results of method executions for requests are serialized
        using this schema.
    """
    def __init__(
            self,
            logger: Logger,
            name: str,
            function: Callable,
            *,
            param_schemas: Optional[List[Optional[marshmallow.Schema]]] = None,
            result_schema: Optional[marshmallow.Schema] = None):
        super().__init__(name)
        self.__function = function
        self.__param_schemas = param_schemas
        self.__result_schema = result_schema
        self.__logger = LabeledLogger(logger, 'mprpc.method.' + name)

    @property
    def function(self) -> Callable:
        """get function

        Returns
        -------
        Callable
            function to call for method execution
        """

        return self.__function

    def process_request(self, session: Session, msg: Request) -> MessageData:
        """process a request

        Parameters
        ----------
        session : Session
            session
        msg : Request
            request message

        Returns
        -------
        MessageData
            response message data
        """

        try:
            params = validate_data_list(msg.params, self.__param_schemas)
            result = self.__function(*params)
            if self.__result_schema:
                result = self.__result_schema.dump(result)
            return pack_response(msg.msgid, result=result)
        except Exception as err:  # pylint: disable=broad-except
            # all exceptions, which may be thrown by user cades, must be processed.
            self.__logger.error('exception thrown in method: %s', err)
            return pack_response(msg.msgid, error=str(err))

    def process_notification(self, session: Session, msg: Notification):
        """process a notification

        Parameters
        ----------
        session : Session
            session
        msg : Notification
            notification message
        """

        try:
            params = validate_data_list(msg.params, self.__param_schemas)
            self.__function(*params)
        except Exception as err:  # pylint: disable=broad-except
            # all exceptions, which may be thrown by user cades, must be processed.
            self.__logger.error('exception thrown in method: %s', err)