class Channel(Queue):
    """
    Channel is the main communication mechanism between actors. It is
    actually a simple facade to the gevent.Queue 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 the constants in util.py (:mod:`pyactor.util`).
        """
        return self.get(timeout=timeout) class PyActorTimeoutError(Exception):
    """Wait time expired."""

    def __init__(self, method='Not specified'):
        self.method = method

    def __str__(self):
        return f"Timeout triggered: {self.method!r}" class FutureError(Exception):
    """Some problem with the Future."""

    def __init__(self, value='Not specified'):
        self.value = value

    def __str__(self):
        return f"Future ERROR: {self.value!r}" TELL = 'TELL' FUTURE = 'FUTURE' TYPE = 'TYPE' METHOD = 'METHOD' PARAMS = 'PARAMS' CHANNEL = 'CHANNEL' TO = 'TO' def get_current():
    if core_type == 'thread':
        current = current_thread()
    else:
        current = getcurrent()
    for host in hosts.values():
        if current in host.threads.keys():
            return host.actors[host.threads[current]]
        elif current in host.pthreads.keys():
            return host.actors[host.pthreads[current]] def get_host():
    if core_type == 'thread':
        current = current_thread()
    else:
        current = getcurrent()
    for host in hosts.values():
        if current in host.threads.keys():
            return host
        elif current in host.pthreads.keys():
            return host
    return main_host RPC_ID = 'RPC_ID' RESULT = 'RESULT'
import uuid
from gevent import spawn
from gevent.event import Event
from .channel import Channel
from ..exceptions import PyActorTimeoutError, FutureError
from ..util import TELL, FUTURE, TYPE, METHOD, PARAMS, CHANNEL, TO
from ..util import get_current, get_host, RPC_ID, RESULT



PENDING = 'PENDING'
RUNNING = 'RUNNING'
FINISHED = 'FINISHED'


class Future(object):
    """
    Container for the result of an ask query sent asynchronously which
    could not be resolved yet.

    :param str. fid: future ID.
    """

    def __init__(self, fid, future_ref, manager_channel):
        self.__condition = Event()
        self.__state = PENDING
        self.__result = None
        self.__exception = None
        self.__callbacks = []

        self.__method = future_ref[METHOD]
        self.__params = future_ref[PARAMS]
        self.__actor_channel = future_ref[CHANNEL]
        self.__target = future_ref[TO]
        self.__channel = manager_channel
        self.__id = fid

    def _invoke_callbacks(self):
        for callback in self.__callbacks:
            try:
                # msg = TellRequest(TELL, callback[0], [self], callback[2])
                msg = {TYPE: TELL, METHOD: callback[0], PARAMS: ([self], {}),
                       TO: callback[2]}
                callback[1].send(msg)
            except Exception as e:
                raise FutureError(
                    f"Exception calling callback for {self!r}: {e!r}")

    def running(self):
        """Return True if the future is currently executing."""
        # with self.__condition:
        return self.__state == RUNNING

    def done(self):
        """Return True if the future finished executing."""
        # with self.__condition:
        return self.__state == FINISHED

    def __get__result(self):
        if self.__exception is not None:
            raise self.__exception
        else:
            return self.__result

    def add_callback(self, method):
        """
        Attaches a method that will be called when the future finishes.

        :param method: A callable from an actor that will be called
            when the future completes. The only argument for that
            method must be the future itself from which you can get the
            result though `future.:meth:`result()``. If the future has
            already completed, then the callable will be called
            immediately.

        .. note:: This functionality only works when called from an actor,
            specifying a method from the same actor.
        """

        from_actor = get_current()