'Wrap subprocess.Popen in such a way that the stderr and stdout from running a shell command will
be captured and logged in nearly real time.  This is similar to fabric.local, but allows us to
retain control over the process.  This method is named "complex" because it uses queues and
threads to execute a command while capturing and displaying the output.'

def handle_complex_command(self, command, job_name=''):

    llog_name = __name__
    if (len(job_name) > 0):
        llog_name += (':' + job_name)
    llog = logging.getLogger(llog_name)
    llog.debug(('+    ' + str(command)))
    process_handle = subprocess.Popen(str(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1, close_fds=False, shell=True, cwd=state.env['lcwd'])
    pid = process_handle.pid
    stdout_queue = queue.Queue()
    stdout_reader = asynchronous_reader.AsynchronousReader(process_handle.stdout, stdout_queue)
    stdout_reader.start()
    stderr_queue = queue.Queue()
    stderr_reader = asynchronous_reader.AsynchronousReader(process_handle.stderr, stderr_queue)
    stderr_reader.start()
    stdio_thread = threading.Thread(target=self.enqueue_output, args=(process_handle.stdout, stdout_queue, process_handle.stderr, stderr_queue))
    thread_lock = threading.Lock()
    thread_lock.acquire()
    stdio_thread.start()
    start_timer = time.time()
    while ((not stdout_reader.installation_complete()) or (not stderr_reader.installation_complete())):
        while (not stdout_queue.empty()):
            try:
                line = stdout_queue.get()
            except queue.Empty:
                line = None
                break
            if line:
                llog.debug(line)
                start_timer = time.time()
            else:
                break
        while (not stderr_queue.empty()):
            try:
                line = stderr_queue.get()
            except queue.Empty:
                line = None
                break
            if line:
                llog.debug(line)
                start_timer = time.time()
            else:
                stderr_queue.task_done()
                break
        time.sleep(0.1)
        current_wait_time = (time.time() - start_timer)
        if (stdout_queue.empty() and stderr_queue.empty() and (current_wait_time > basic_util.NO_OUTPUT_TIMEOUT)):
            err_msg = ('\nShutting    down    process    id    %s    because    it    generated    no    output    for    the    defined    timeout    period    of    %.1f    seconds.\n' % (pid, basic_util.NO_OUTPUT_TIMEOUT))
            stderr_reader.lines.append(err_msg)
            process_handle.kill()
            break
    thread_lock.release()
    stdio_thread.join(basic_util.NO_OUTPUT_TIMEOUT)
    stdout_reader.join(basic_util.NO_OUTPUT_TIMEOUT)
    stderr_reader.join(basic_util.NO_OUTPUT_TIMEOUT)
    self.close_file_descriptor(process_handle.stdout)
    self.close_file_descriptor(process_handle.stderr)
    stdout = '\n'.join(stdout_reader.lines)
    stderr = '\n'.join(stderr_reader.lines)
    output = _AttributeString((stdout.strip() if stdout else ''))
    errors = _AttributeString((stderr.strip() if stderr else ''))
    process_handle.poll()
    output.return_code = process_handle.returncode
    output.stderr = errors
    return output
