import os
import subprocess
import errno
import fcntl

BUFSIZ = 1024

def _set_nonblocking(fd):
    flags = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)

"""A non-blocking version of subprocess.check_output on the tornado ioloop."""
def check_output(call, callback, ioloop):
    out_r, out_w = os.pipe()
    nul_f = open(os.devnull, 'w')
    p = subprocess.Popen(call, stdout=out_w, stderr=nul_f)
    nul_f.close()
    os.close(out_w)
    _set_nonblocking(out_r)
    data = []

    def _poll():
        if p.returncode is None:
            p.poll()

        if p.returncode is not None:
            ioloop.remove_handler(out_r)
            try:
                buf = os.read(out_r, BUFSIZ)
            except (IOError, OSError) as e:
                pass
            os.close(out_r)

            if buf:
                data.append(buf)
            callback("".join(data), p.returncode)

    def _handle_read(fd, events):
        if events & ioloop.READ:
            try:
                buf = os.read(out_r, BUFSIZ)
            except (IOError, OSError) as e:
                if e.args[0] == errno.EBADF:
                    _poll()
                elif e.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN):
                    raise

            if not buf:
                _poll()
            else:
                data.append(buf)

        if events & ioloop.ERROR:
            _poll()

    ioloop.add_handler(out_r, _handle_read,
                       ioloop.ERROR | ioloop.READ)
