Create needed file descriptors (pipes, mostly) before forking.

def prepare_fds (self):
        """Create needed file descriptors (pipes, mostly) before forking."""
        if '_in' in self.options:
            i= self.options['_in']
            logger.debug ('_in: %r', i)

            # this condition is complex, but basically handles any type of input
            # that is not going to be handled later in client()
            if (     not isinstance (i, io.IOBase)      # file and such objects
                 and type (i) not in (int, str, bytes)  # fd's and file names
                 and i is not None                      # special value for /dev/null
                 and not isinstance (i, Command) ):     # Command's
                if self.options['_in_tty']:
                    # TODO: no support yet for input from file when _in_tty
                    # NOTE: os.openpty() returns (master, slave)
                    # but when writing master=w, slave=r
                    logger.debug ('_in: using tty')
                    (master, slave)= os.openpty ()
                    self.stdin_pipe= (slave, master)
                else:
                    logger.debug ("_in:%s creates a pipe()", type (i))
                    self.stdin_pipe= os.pipe ()
            elif isinstance (i, Command):
                if i.options.get ('_out', None)==Capture:
                    # if it's a captured command, create a pipe to feed the data
                    logger.debug ("_in::Command, _in._out==Capture creates a pipe()")
                    self.stdin_pipe= os.pipe ()
                elif i.options.get ('_out', None)==Pipe:
                    # if it's a piped command, use its pipe and hope it runs in the bg
                    logger.debug ("_in::Command uses the stdout_pipe")
                    self.stdin_pipe= i.stdout_pipe

        logger.debug ("stdin_pipe: %s", self.stdin_pipe)

        if '_out' in self.options:
            if self.options['_out']==Capture:
                if self.options['_out_tty']:
                    if self.options['_in_tty']:
                        # we use a copy of the pty created for the stdin
                        logger.debug ('duping tty form _in to _out')
                        self.stdout_pipe= (os.dup (self.stdin_pipe[1]),
                                           os.dup (self.stdin_pipe[0]))
                    else:
                        # this time the order is right
                        logger.debug ('_out: using tty')
                        (master, slave)= os.openpty ()
                        self.stdout_pipe= (master, slave)
                else:
                    logger.debug ("_out==Capture creates a pipe()")
                    self.stdout_pipe= os.pipe ()
            elif self.options['_out']==Pipe:
                # this pipe should be picked up by the outer Command
                logger.debug ("_out==Pipe creates a pipe()")
                self.stdout_pipe= os.pipe ()

        logger.debug ("stdout_pipe: %s", self.stdout_pipe)

        if '_err' in self.options:
            if self.options['_err']==Capture:
                # if stdout is also Capture'd, then use the same pipe
                if not '_out' in self.options or self.options['_out']!=Capture:
                    # if stdout is a tty, hook to that one
                    if self.options['_out_tty']:
                        logger.debug ('duping tty form _out to _err')
                        self.stderr_pipe= (os.dup (self.stdout_pipe[0]),
                                           os.dup (self.stdout_pipe[1]))
                    else:
                        logger.debug ("_err==Capture creates a pipe()")
                        self.stderr_pipe= os.pipe ()
            elif self.options['_err']==Pipe:
                # this pipe should be picked up by the outer Command
                logger.debug ("_err==Pipe creates a pipe()")
                self.stderr_pipe= os.pipe ()

        logger.debug ("stderr_pipe: %s", self.stderr_pipe)