class Actor(ActorRef):
    """
    Actor is the instance of an object to which is possible to access
    and invoke its methods remotely. Main element of the model. The
    host is the one to create them (spawning -> see :meth:`~.spawn`).

    :param str. url: URL where the actor is running.
    :param class klass: class type for the actor.
    :param klass obj: instance of the *klass* class to attach to the
        actor.
    """

    def __init__(self, url, klass, obj):
        super(Actor, self).__init__(url, klass)
        self._obj = obj
        self.id = obj.id
        self.running = True
        self.thread = None
        self.future_manager = FutureManager()

    def __process_queue(self):
        while self.running:
            message = self.channel.receive()
            self.receive(message)

    def is_alive(self):
        """
        :return: (*bool.*) identifies the current state of the actor.
            **True** if it is running.
        """
        return self.running

    def receive(self, msg):
        """
        The message received from the queue specifies a method of the
        class the actor represents. This invokes it. If the
        communication is an ASK, sends the result back
        to the channel included in the message as an ASK_RESPONSE.

        If it is a FUTURE, generates a FUTURE_RESPONSE
        to send the result to the manager.

        :param msg: The message is a dictionary using the constants
            defined in util.py (:mod:`pyactor.util`).
        """
        if msg[TYPE] == TELL and msg[METHOD] == 'stop':
            self.running = False
            self.future_manager.stop()
        else:
            try:
                invoke = getattr(self._obj, msg[METHOD])
                params = msg[PARAMS]
                result = invoke(*params[0], **params[1])
            except Exception as e:
                if msg[TYPE] == TELL:
                    print(e)
                    return
                result = e
            self.send_response(result, msg)

    def send_response(self, result, msg):
        if msg[TYPE] == ASK:
            response = {TYPE: ASK_RESPONSE, RESULT: result,
                        RPC_ID: msg[RPC_ID] if RPC_ID in msg.keys() else None}
            msg[CHANNEL].send(response)
        elif msg[TYPE] == FUTURE:
            response = {TYPE: FUTURE_RESPONSE, RPC_ID: msg[RPC_ID],
                        RESULT: result}
            msg[CHANNEL].send(response)

    def run(self):
        """
        Creates the actor thread which will process the channel queue
        while the actor :meth:`is_alive`, making it able to receive
        queries.
        """
        self.thread = Thread(target=self.__process_queue)
        self.thread.start() class Channel(Queue):
    """
    Channel is the main communication mechanism between actors. It is
    actually a simple facade to the queue.Queue python class.
    """

    def __init__(self):
        Queue.__init__(self)

    def send(self, msg):
        """
        It sends a message to the current channel.

        :param msg: The message sent to an actor. It is a dictionary using
            the constants in util.py (:mod:`pyactor.util`).
        """
        self.put(msg)

    def receive(self, timeout=None):
        """
        It receives a message from the channel, blocking the calling
        thread until the response is received, or the timeout is
        triggered.

        :param int timeout: timeout to wait for messages. If none
            provided it will block until a message arrives.
        :return: returns a message sent to the channel. It is a dictionary
            using the constants in util.py (:mod:`pyactor.util`).
        """
        return self.get(timeout=timeout) FUTURE = 'FUTURE' ASK_RESPONSE = 'ASK_RESPONSE' FUTURE_RESPONSE = 'FUTURE_RESPONSE' TYPE = 'TYPE' METHOD = 'METHOD' TELL = 'TELL' ASK = 'ASK' CHANNEL = 'CHANNEL' FROM = 'FROM' TO = 'TO' RPC_ID = 'RPC_ID'
import uuid
import pickle
import traceback
from urllib.parse import urlparse
from importlib import import_module
from .actor import Actor
from .channel import Channel
from ..util import FUTURE, ASK_RESPONSE, FUTURE_RESPONSE
from ..util import TYPE, METHOD, TELL, ASK, CHANNEL, FROM, TO, RPC_ID



class RPCDispatcher(Actor):
    """
    This is the actor that will manage remote sends and receives
    with other hosts. Each host has one, configured depending on
    the scheme specified when created.
    """

    def __init__(self, url, host, mode):
        self.server_model = import_module('pyactor.' + mode + 'server')
        self.url = url
        self.host = host
        aurl = urlparse(url)
        address = aurl.netloc.split(':')
        ip, port = address[0], address[1]
        self.source = self.server_model.Source((ip, int(port)))
        self.source.register_function(self.on_message)
        self.source.start()
        self.running = True

        self.channel = Channel()