class Sandbox(object):

    def __init__(self):
        self._events = dict()
        self._logger = Logger()

    def invoke(self, event_name, request, stream):
        """ Connect worker and decorator """
        event_closure = self._events.get(event_name, None)
        if event_closure is not None:
            event_handler = event_closure()
            event_handler.invoke(request, stream)
        else:
            self._logger.warn("There is no handler for event %s" % event_name)

    def on(self, event_name, event_handler):
        try:
            # Try to construct handler.
            closure = event_handler()
        except Exception:
            # If this callable object is not our wrapper - may raise Exception
            closure = default(event_handler)()
            if hasattr(closure, "_wrapped"):
                event_handler = default(event_handler)
        else:
            if not hasattr(closure, "_wrapped"):
                event_handler = default(event_handler)
        self._events[event_name] = event_handler
class Request(object):

    def __init__(self):
        self._logger = Logger()
        self.cache = list()
        self._clbk = None   # Callback - on chunk
        self._errbk = None  # Errorback - translate error to handler
        self._errmsg = None # Store message
        self._state = 1     # Status of stream (close/open)

    def push(self, chunk):
        if self._clbk is None:
            # If there is no attachment object, put chunk in the cache
            self._logger.debug("Cache chunk")
            self.cache.append(chunk)
        else:
            # Copy callback to temp, clear current callback and perform temp
            # Do it so because self._clbk may change, while perfoming callback function.
            # Avoid double chunk sending to the task
            self._logger.debug("Send chunk to application")
            temp = self._clbk
            self._clbk = None
            temp(chunk)

    def error(self, errormsg):
        self._errmsg = errormsg

    def close(self):
        self._logger.debug("Close request")
        self._state = None
        if len(self.cache) == 0 and self._clbk is not None:
            self._logger.warn("Chunks are over, but the application requests them")
            if self._errbk is not None:
                self._logger.error("Throw error")
                self._errbk(RequestError("No chunks are available"))
            else:
                self._logger.error("No errorback. Can't throw error")


    def read(self):
        def wrapper(clbk, errorback=None):
            self._read(clbk, errorback)
        return wrapper

    def _read(self, callback, errorback):
        if len(self.cache) > 0:
            callback(self.cache.pop(0))
        elif self._errmsg is not None:
            errorback(self._errmsg) #traslate error into worker
        elif self._state is not None:
            self._clbk = callback
            self._errbk = errorback
        else:
            #Stream closed by choke
            #Raise exception here because no chunks from cocaine-runtime are availaible
            self._logger.warn("Chunks are over, but the application requests them")
            errorback(RequestError("No chunks are available"))