Example #1
0
 def _delay(self):
     d = Deferred()
     ioloop.IOLoop.current().add_timeout(timedelta(seconds=self._cur_delay),
                                         lambda: d.trigger(None))
     self.logger.debug('Delaying for {:.2f} s'.format(self._cur_delay))
     yield d
     self.logger.debug('Resuming from delay...')
     self._cur_delay = min(self._cur_delay * self.delay_exp, self.max_delay)
Example #2
0
 def _delay(self):
     d = Deferred()
     ioloop.IOLoop.current().add_timeout(timedelta(seconds=self._cur_delay),
                                         lambda: d.trigger(None))
     self.logger.debug('Delaying for {:.2f} s'.format(self._cur_delay))
     yield d
     self.logger.debug('Resuming from delay...')
     self._cur_delay = min(self._cur_delay * self.delay_exp, self.max_delay)
Example #3
0
    def __init__(self, sock, ioLoop=None):
        self.address = None

        self.sock = sock
        self.sock.setblocking(False)
        if self.sock.type == socket.SOL_TCP:
            self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        fcntl.fcntl(self.sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)

        self._ioLoop = ioLoop or Loop.instance()

        self._state = self.NOT_CONNECTED
        self._onConnectedDeferred = Deferred()
        self._connectionTimeoutTuple = None
        def wrapper(*args, **kwargs):
            future = Deferred()
            timeout = kwargs.get('timeout', None)
            if timeout is not None:
                timeoutId = self._ioLoop.add_timeout(time() + timeout, lambda: future.error(TimeoutError(timeout)))

                def timeoutRemover(func):
                    def wrapper(*args, **kwargs):
                        self._ioLoop.remove_timeout(timeoutId)
                        return func(*args, **kwargs)
                    return wrapper
                future.close = timeoutRemover(future.close)
            self._session += 1
            self._writableStream.write([methodId, self._session, args])
            self._subscribers[self._session] = future
            return Chain([lambda: future], ioLoop=self._ioLoop)
Example #5
0
        def wrapper(*args, **kwargs):
            future = Deferred()
            timeout = kwargs.get('timeout', None)
            if timeout is not None:
                timeoutId = self._ioLoop.add_timeout(
                    time() + timeout,
                    lambda: future.error(TimeoutError(timeout)))

                def timeoutRemover(func):
                    def wrapper(*args, **kwargs):
                        self._ioLoop.remove_timeout(timeoutId)
                        return func(*args, **kwargs)

                    return wrapper

                future.close = timeoutRemover(future.close)
            self._session += 1
            self._writableStream.write([methodId, self._session, args])
            self._subscribers[self._session] = future
            return Chain([lambda: future], ioLoop=self._ioLoop)
Example #6
0
    def connect(self, address, timeout=None, blocking=False):
        if self.isConnecting():
            return self._onConnectedDeferred

        if self.isConnected():
            raise IllegalStateError('already connected')

        self._onConnectedDeferred = Deferred()
        self._state = self.CONNECTING
        if blocking:
            return self._blockingConnect(address, timeout)
        else:
            return self._nonBlockingConnect(address, timeout)
Example #7
0
    def __init__(self, sock, ioLoop=None):
        self.address = None

        self.sock = sock
        self.sock.setblocking(False)
        if self.sock.type == socket.SOL_TCP:
            self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        fcntl.fcntl(self.sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)

        self._ioLoop = ioLoop or Loop.instance()

        self._state = self.NOT_CONNECTED
        self._onConnectedDeferred = Deferred()
        self._connectionTimeoutTuple = None
Example #8
0
class Pipe(object):
    NOT_CONNECTED, CONNECTING, CONNECTED = range(3)

    def __init__(self, sock, ioLoop=None):
        self.address = None

        self.sock = sock
        self.sock.setblocking(False)
        if self.sock.type == socket.SOL_TCP:
            self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        fcntl.fcntl(self.sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)

        self._ioLoop = ioLoop or Loop.instance()

        self._state = self.NOT_CONNECTED
        self._onConnectedDeferred = None
        self._connectionTimeoutTuple = None

    def fileno(self):
        return self.sock.fileno()

    def isConnected(self):
        return self._state == self.CONNECTED

    def isConnecting(self):
        return self._state == self.CONNECTING

    def connect(self, address, timeout=None, blocking=False):
        if self.isConnecting():
            return self._onConnectedDeferred

        if self.isConnected():
            raise IllegalStateError('already connected')

        self._onConnectedDeferred = Deferred()
        self._state = self.CONNECTING
        if blocking:
            return self._blockingConnect(address, timeout)
        else:
            return self._nonBlockingConnect(address, timeout)

    def _blockingConnect(self, address, timeout=None):
        try:
            self.sock.settimeout(timeout)
            self.sock.connect(address)
            self.address = address
            self._state = self.CONNECTED
        except socket.error as err:
            if err.errno == errno.ECONNREFUSED:
                raise ConnectionRefusedError(address)
            elif err.errno == errno.ETIMEDOUT:
                raise ConnectionTimeoutError(address, timeout)
            else:
                raise ConnectionError(address, err)
        finally:
            self.sock.setblocking(False)

    def _nonBlockingConnect(self, address, timeout):
        try:
            self.sock.connect(address)
        except socket.error as err:
            if err.errno in (errno.EINPROGRESS, errno.EWOULDBLOCK):
                callback = functools.partial(self._onConnectedCallback, address)
                self._ioLoop.add_handler(self.sock.fileno(), callback, self._ioLoop.WRITE)
                if timeout:
                    start = time.time()
                    errorback = functools.partial(self._onConnectionTimeout, address)
                    timeoutId = self._ioLoop.add_timeout(start + timeout, errorback)
                    self._connectionTimeoutTuple = timeoutId, start, timeout
            else:
                log.warning('connect error on fd {0}: {1}'.format(self.sock.fileno(), err))
                self.close()
                self._ioLoop.add_callback(lambda: self._onConnectedDeferred.error(ConnectionError(address, err)))
        return self._onConnectedDeferred

    def close(self):
        if self._state == self.NOT_CONNECTED:
            return
        self._state = self.NOT_CONNECTED
        self.address = None
        self._ioLoop.stop_listening(self.sock.fileno())
        self.sock.close()
        self.sock = None

    def _onConnectionTimeout(self, address):
        if self._connectionTimeoutTuple:
            timeoutId, start, timeout = self._connectionTimeoutTuple
            self._ioLoop.remove_timeout(timeoutId)
            self._connectionTimeoutTuple = None
            self.close()
            df = self._onConnectedDeferred
            self._onConnectedDeferred = None
            df.error(ConnectionTimeoutError(address, timeout))

    def _onConnectedCallback(self, address, fd, event):
        assert fd == self.sock.fileno(), 'Incoming fd must be socket fd'
        assert (event & self._ioLoop.WRITE) or (event & self._ioLoop.ERROR), 'Event must be either write or error'

        def removeConnectionTimeout():
            if self._connectionTimeoutTuple:
                fd, start, timeout = self._connectionTimeoutTuple
                self._ioLoop.remove_timeout(fd)
                self._connectionTimeoutTuple = None

        df = self._onConnectedDeferred
        self._onConnectedDeferred = None
        err = self.sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
        if err == 0:
            self._state = self.CONNECTED
            self.address = address
            removeConnectionTimeout()
            self._ioLoop.stop_listening(self.sock.fileno())
            df.trigger(None)
        elif err not in (errno.EINPROGRESS, errno.EAGAIN, errno.EALREADY):
            self.close()
            removeConnectionTimeout()
            df.error(ConnectionError(address, os.strerror(err)))

    def read(self, buff, size):
        return self._handle(self.sock.recv_into, buff, size)

    def write(self, buff):
        return self._handle(self.sock.send, buff)

    def _handle(self, func, *args):
        try:
            return func(*args)
        except socket.error as e:
            if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK):
                return 0
            elif e.errno in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE):
                self.close()
                return 0
            else:
                raise

    def __del__(self):
        self.close()
Example #9
0
def async_subprocess(command, callbacks=None, cwd=None, io_loop=None):
    """Run subprocess asynchronously and get bound `Deferred` object.

    This function runs separate subprocess `command` and attaches to the standard output stream (`stdout`) and
    error stream (`stderr`), providing ability to read them asynchronously.

    This can be useful when running multiple subprocesses simultaneously, e.g. in web server.

    You can attach up to two callbacks as list for `callbacks` parameter, first of them will be callback for `stdout`,
    second - for `stderr`.

    For example::

        engine.subprocess(['echo 123'], callbacks=[sys.stdout.write, None])

    means `sys.stdout` function as callback for `stdout` and there will be no callback for `stderr`.

    Returned `Deferred` object will trigger immediately after subprocess is finished, transferring error code as
    parameter. If process exits with error code differed from 0 an `IOError` exception will be thrown.

    An subprocess pipe exception will be raised if subprocess can not be started.

    .. note:: You can `yield` this function in `engine.asynchronous` context.

    :param command: command for subprocess to start, same as `subprocess.Popen` first argument.
    :param callbacks: list of two callbacks for `stdout` and `stderr` respectively. If you don't want to attach
                      any callback, you can pass `None` as function.
    :param cwd: current working directory for subprocess.
    :param io_loop: tornado event loop, current by default.
    """
    io_loop = io_loop or IOLoop.current()
    PIPE = subprocess.PIPE
    process = subprocess.Popen(command,
                               shell=True,
                               stdin=PIPE,
                               stdout=PIPE,
                               stderr=PIPE,
                               close_fds=True,
                               cwd=cwd)

    fhs = [process.stdout, process.stderr]
    deferred = Deferred()

    def create_handler(fh, callback):
        def handle(fd, events):
            assert events == io_loop.READ
            data = fh.readline()
            if callback is not None and data:
                callback(data)

            if process.poll() is not None:
                io_loop.remove_handler(fd)
                if process.returncode == 0:
                    deferred.trigger(process.returncode)
                else:
                    deferred.error(IOError(process.returncode))

        return handle

    for fh, callback in zip(fhs, callbacks):
        io_loop.add_handler(fh.fileno(), create_handler(fh, callback),
                            io_loop.READ)
    return deferred
Example #10
0
class Pipe(object):
    NOT_CONNECTED, CONNECTING, CONNECTED = range(3)

    def __init__(self, sock, ioLoop=None):
        self.address = None

        self.sock = sock
        self.sock.setblocking(False)
        if self.sock.type == socket.SOL_TCP:
            self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        fcntl.fcntl(self.sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)

        self._ioLoop = ioLoop or Loop.instance()

        self._state = self.NOT_CONNECTED
        self._onConnectedDeferred = Deferred()
        self._connectionTimeoutTuple = None

    def fileno(self):
        return self.sock.fileno()

    def isConnected(self):
        return self._state == self.CONNECTED

    def isConnecting(self):
        return self._state == self.CONNECTING

    def connect(self, address, timeout=None, blocking=False):
        if self.isConnecting():
            return self._onConnectedDeferred

        if self.isConnected():
            raise IllegalStateError('already connected')

        self._state = self.CONNECTING
        if blocking:
            return self._blockingConnect(address, timeout)
        else:
            return self._nonBlockingConnect(address, timeout)

    def _blockingConnect(self, address, timeout=None):
        try:
            self.sock.settimeout(timeout)
            self.sock.connect(address)
            self.address = address
            self._state = self.CONNECTED
        except socket.error as err:
            if err.errno == errno.ECONNREFUSED:
                raise ConnectionRefusedError(address)
            elif err.errno == errno.ETIMEDOUT:
                raise ConnectionTimeoutError(address, timeout)
            else:
                raise ConnectionError(address, err)
        finally:
            self.sock.setblocking(False)

    def _nonBlockingConnect(self, address, timeout):
        try:
            self.sock.connect(address)
        except socket.error as err:
            if err.errno in (errno.EINPROGRESS, errno.EWOULDBLOCK):
                callback = functools.partial(self._onConnectedCallback,
                                             address)
                self._ioLoop.add_handler(self.sock.fileno(), callback,
                                         self._ioLoop.WRITE)
                if timeout:
                    start = time.time()
                    errorback = functools.partial(self._onConnectionTimeout,
                                                  address)
                    timeoutId = self._ioLoop.add_timeout(
                        start + timeout, errorback)
                    self._connectionTimeoutTuple = timeoutId, start, timeout
            else:
                log.warning('connect error on fd {0}: {1}'.format(
                    self.sock.fileno(), err))
                self.close()
                self._ioLoop.add_callback(lambda: self._onConnectedDeferred.
                                          error(ConnectionError(address, err)))
        return self._onConnectedDeferred

    def close(self):
        if self._state == self.NOT_CONNECTED:
            return
        self._state = self.NOT_CONNECTED
        self.address = None
        self._ioLoop.stop_listening(self.sock.fileno())
        self.sock.close()
        self.sock = None

    def _onConnectionTimeout(self, address):
        if self._connectionTimeoutTuple:
            timeoutId, start, timeout = self._connectionTimeoutTuple
            self._ioLoop.remove_timeout(timeoutId)
            self._connectionTimeoutTuple = None
            self.close()
            self._onConnectedDeferred.error(
                ConnectionTimeoutError(address, timeout))

    def _onConnectedCallback(self, address, fd, event):
        assert fd == self.sock.fileno(), 'Incoming fd must be socket fd'
        assert (event & self._ioLoop.WRITE) or (
            event & self._ioLoop.ERROR), 'Event must be either write or error'

        def removeConnectionTimeout():
            if self._connectionTimeoutTuple:
                fd, start, timeout = self._connectionTimeoutTuple
                self._ioLoop.remove_timeout(fd)
                self._connectionTimeoutTuple = None

        err = self.sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
        if err == 0:
            self._state = self.CONNECTED
            self.address = address
            removeConnectionTimeout()
            self._ioLoop.stop_listening(self.sock.fileno())
            self._onConnectedDeferred.trigger(None)
        elif err not in (errno.EINPROGRESS, errno.EAGAIN, errno.EALREADY):
            self.close()
            removeConnectionTimeout()
            self._onConnectedDeferred.error(
                ConnectionError(address, os.strerror(err)))

    def read(self, buff, size):
        return self._handle(self.sock.recv_into, buff, size)

    def write(self, buff):
        return self._handle(self.sock.send, buff)

    def _handle(self, func, *args):
        try:
            return func(*args)
        except socket.error as e:
            if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK):
                return 0
            elif e.errno in (errno.ECONNRESET, errno.ECONNABORTED,
                             errno.EPIPE):
                self.close()
                return 0
            else:
                raise

    def __del__(self):
        self.close()