import sys
import os
import threading
import logging

from vsi.tools.python import Try

class RedirectBase(object):
  def flush(self):
    ''' Flushes stdout and stderr
    '''
    sys.stdout.flush()
    sys.stderr.flush()

class Logger(object):
  ''' File object wrapper for logger.

      Careful when using this, if the logging out goes to a stream that is
      redireted, you have an infinite capture loop and does not go well

      Use PopenRedirect instead of Redirect in that case
  '''

  def __init__(self, logger, lvl=logging.INFO):
    ''' Create a wrapper for logger using lvl level

        Parameters
        ----------
        logger : str
            The logger
        lvl : int
            Logging Level
    '''

    self.logger=logger
    self.lvl = lvl

  def write(self, str):
    ''' Write method

        Parameters
        ----------
        str : str
            The String
    '''
    str = str.rstrip()
    if str:
      self.logger.log(self.lvl, str)

class FileRedirect(object):
  ''' Redirect a real file object to any object with .write

      Some function need a REAL file object with file number and all. This
      class wraps a pair of pipes per output and so what everytime the pipe
      is written to, output.write is called.

      Only supports one way communication. You can write to the file object
      via any valid call, and the other end of the pipe performs "readlines"
      and calls output.write() It would be possible to expand this to two way,
      but not currently needed.

      Primarily used in a with expression:

      >>> from StringIO import StringIO
      >>> s = StringIO()
      >>> with FileRedirect([s]) as f:
      >>>   f.wids[0].write('hiya')
      >>> s.seek(0); print(s.read())

      IF you call __enter__ manually, you only need to close the wids file
      objects, and the rest of the threads and file objects are cleaned up,
      of course it would be better to call __exit__ in that case.
  '''
  def __init__(self, outputs=[]):

    self.outputs = outputs
    ''' Create a FileRedirect

        Parameters
        ----------
        outputs : list
            list of outputs objects to write to. For every output in outputs,
            a rid in self.rids and wid in self.wids is created when call with
            with

        Yields
        ------
        list
            list of outputs objects.
    '''

  def __enter__(self):
    ''' Create the pipes and monitoring threads '''
    self.rids = []
    self.wids = []
    self.tids = []

    for index, output in enumerate(self.outputs):
      r, w = os.pipe()
      self.rids.append(os.fdopen(r, 'rb'))
      self.wids.append(os.fdopen(w, 'wb'))

      self.startMonitor(index)

    return self

  def __exit__(self, exc_type=None, exc_value=None, traceback=None):
    ''' Closes the write ends of the pipes, and join with monitoring threads

        The read pipes are automatically closed by the monitors

        Parameters
        ----------
        exc_type : str
            The Exception Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    '''
    for wid in self.wids:
      wid.close()

    for tid in self.tids:
      tid.join()

  def __bleed(self, streamIndex):

    rid = self.rids[streamIndex]
    wid = self.wids[streamIndex]
    output = self.outputs[streamIndex]
    ''' Function to read all the data and write to output

        Automactically closes read pipe when done. Can only be stopped by
        closing the write end of pipe.

        Parameters
        ----------
        streamIndex : int
            The Stream Index
    '''
    #do-while
    chunk = True
    while chunk: #while rid isn't closed
      chunk = rid.readline(64*1024) #read a chunk
      output.write(chunk) #write chunk
    rid.close()

  def startMonitor(self, stream_index):
    ''' Start a new thread to monitor the stream

        Should only be called once per stream_index

        Parameters
        ----------
        stream_index : int
            The Stream Index
    '''
    self.tids.append(threading.Thread(target=self.__bleed, args=(stream_index,)))
    self.tids[-1].start()

class PopenRedirect(FileRedirect):
  ''' Version FileRedirect object geared towards redirecting for Popen commands

      >>> from StringIO import StringIO
      >>> from subprocess import Popen
      >>> out = StringIO(); err = StringIO()
      >>> with PopenRedirect(out, err) as f:
      >>>   Popen(['whoami'], stdout=f.stdout, stderr=f.stderr).wait()
      >>>   Popen(['whoami', 'non user'], stdout=f.stdout, stderr=f.stderr).wait()
      >>> out.seek(0); err.seek(0)
      >>> print(out.read(), err.read())
  '''
  def __init__(self, stdout=type('Foo', (object,), {'write':lambda x,y:None})(),
                     stderr=type('Bar', (object,), {'write':lambda x,y:None})()):
    ''' Create PopenRedirect object

        Parameters
        ----------
        stdout : :term:`file-like object`
            Stdout File like object, must have .write method
        stderr : :term:`file-like object`
            Stderr File like object, must have .write method
    '''
    self.stdout_output = stdout
    self.stderr_output = stderr

    super(PopenRedirect, self).__init__([stdout, stderr])

  @property
  def stdout(self):
    ''' Returns
        -------
        str
            First object in wireless intrusion prevention system list.
    '''
    return self.wids[0]

  @property
  def stderr(self):
    ''' Returns
        -------
        str
            Second object in wireless intrusion prevention system list.
    '''
    return self.wids[1]

class Redirect(RedirectBase): #Version 2
  ''' FAILED EXPERIMENT! You can use this, but I don't recommend it for
      anything that matters. There are too many situations where buffered
      output or multiple stdouts in windows cause this to not behave 100%
      right. Best to just not use it. PopenRedirect is good

      MORE FAILURES! It looks like when extending python with C, too many
      cout/flushes can result in a deadlock, I'm guess the GIL does something
      weird

      Similar to Capture class, except it redirect to file like objects

      There are times in python when using "those kind of libraries"
      where you have to capture either stdout or stderr, and many
      situations are tricky. This class will help you, by using a with
      statement.

      Example::

          from StringIO import StringIO
          stdout = StringIO()
          with Redirect(stdout=stdout):
            some_library.call157()

          stdout.seek(0,0)
          print(stdout.read())

      There are 4 output pipe of concern, none of which are synced with
      each other (flushed together).

      stdout and stderr from c (referred to as stdout_c, stderr_c) and
      stdout and stderr from python (referred to as stdout_py, stderr_py).

      Most python functions use the python stdout/stderr, controlled by
      sys.stdout and sys.stderr. However c embedded calls are used the
      c stdout/stderr, usually fd 1 and 2. Because of this, there are 4
      streams to consider

      Known bugs: In windows, after stdout_c is done being redirected, stdout
      buffering appears to no longer be disabled in interactive mode. What this
      means is typing

      >>> print(123)

      No longer returns 123 right away, without a

      >>> sys.stdout.flush()

      Why? I'm guessing os.dup2 breaks the buffered mode. The fix is to

      >>> sys.stdout = os.fdopen(sys.__stdout__.fileno(), 'w', 0)

      However this can only be done once or twice, and then starts failing.

      Work around is to redirect stdout when calling the command.
      For example 'python | find /v ""' Mostly works.

      Basically don't do this in Windows Command line
  '''

  def __init__(self,
               all=None,
               stdout=None,
               stderr=None,
               c=None, py=None,
               stdout_c=None, stderr_c=None,
               stdout_py=None, stderr_py=None,
               stdout_c_fd=1, stderr_c_fd=2,
               stdout_py_module=sys,    stderr_py_module=sys,
               stdout_py_name='stdout', stderr_py_name='stderr'):

    #copy original file information
    self.stdout_c_fd=stdout_c_fd
    self.stderr_c_fd=stderr_c_fd
    self.stdout_py_module=stdout_py_module
    self.stdout_py_name=stdout_py_name
    self.stderr_py_module=stderr_py_module
    self.stderr_py_name=stderr_py_name

    #Determine the "who gets router who" pattern
    self.stdout_c_out = stdout_c
    self.stderr_c_out = stderr_c
    self.stdout_py_out = stdout_py
    self.stderr_py_out = stderr_py

    ''' Create the Redirect object

        Parameters
        ----------
        file_like : :term:`file-like object`, optional
            File Output Argument. All File output arguments should use File
            like Python objects that have a .write call. Many of the arguments
            override the other argument for ease of use
        all : :class:`str`, optional
            Output stdout_c, stderr_c, stdout_py and stderr_py to the all file.
        stdout : :class:`str`, optional
            Output stdout_c and stdout_py to the stdout file.
        stderr : :class:`str`, optional
            Output stderr_c and stderr_py to the stderr file.
        c : :class:`str`, optional
            Output stdout_c and stderr_c to the c file.
        py : :class:`str`, optional
            Output stdout_py and stderr_py to the py file.
        stdout_c : :class:`str`, optional
        stderr_c : :class:`str`, optional
        stdout_py : :term:`file-like object`, optional
        stderr_py : :term:`file-like object`, optional
            Output to each individual stream for maximum customization.
        stdout_c_fd : :class:`int`, optional
        stderr_c_fd : :class:`int`, optional
             The default file number used for stdout (1) and stderr (2). There
             should be no reason to override this
        stdout_py_module : :term:`module`, optional
        stderr_py_module : :term:`module`, optional
        stdout_py_name : :class:`str`, optional
        stderr_py_name : :class:`str`, optional
            Because of the nature of python, in order to replace and restore
            the python object, the module and name of attribute must be passed
            through, where name is a string and module and the actual module.
            The default is sys module and "stdout" or "stderr" (Including the
            quotes). Again, there should be no real reason to override these,
            unless you are doing some IPython/colorama redirecting, or any
            other library that messes with sys.stdout/sys.stderr
    '''

    if c is not None:
      self.stdout_c_out = c
      self.stderr_c_out = c

    if py is not None:
      self.stdout_py_out = py
      self.stderr_py_out = py

    if stdout is not None:
      self.stdout_c_out = stdout
      self.stdout_py_out = stdout

    if stderr is not None:
      self.stderr_c_out = stderr
      self.stderr_py_out = stderr

    if all is not None:
      self.stdout_c_out = all
      self.stderr_c_out = all
      self.stdout_py_out = all
      self.stderr_py_out = all

    #Determine output set
    outputs = set((self.stdout_c_out,
                   self.stderr_c_out,
                   self.stdout_py_out,
                   self.stderr_py_out))
    with Try(KeyError):
      outputs.remove(None)
    #Now output contains a unique list of outputs, not including None

    self.inputs_c = []
    self.inputs_py_module = []
    self.inputs_py_name = []
    self.outputs = []

    #Map out inputs and outputs
    for output in outputs:
      self.inputs_c.append([])
      self.inputs_py_module.append([])
      self.inputs_py_name.append([])
      self.outputs.append(output)

      if self.stdout_c_out == output:
        self.inputs_c[-1].append(self.stdout_c_fd)
      if self.stderr_c_out == output:
        self.inputs_c[-1].append(self.stderr_c_fd)
      if self.stdout_py_out == output:
        self.inputs_py_module[-1].append(self.stdout_py_module)
        self.inputs_py_name[-1].append(self.stdout_py_name)
      if self.stderr_py_out == output:
        self.inputs_py_module[-1].append(self.stderr_py_module)
        self.inputs_py_name[-1].append(self.stderr_py_name)

    super(Redirect, self).__init__()

  def __enter__(self):
    ''' enter function for with statement.

        Switched out stderr and stdout, and starts pipe threads
    '''

    #Clear initial arrays
    self.tids = [] #List of threads running __bleed
    self.writer_fids = [] # list of file objects for the write_pipes

    #Flush
    self.flush()

    self.copies_c = []
    self.copies_py = []
    for output,inputs_c, inputs_py_module, inputs_py_name in zip(self.outputs, self.inputs_c, self.inputs_py_module, self.inputs_py_name):
      #Create the paired pipes
      (reader,writer_c) = os.pipe()
      writer_fid = os.fdopen(writer_c, 'wb')
      self.writer_fids.append(writer_fid)
      #To assist in cleanup in __exit__

      self.startMonitor(reader, output)

      self.copies_c.append([])
      for input_c in inputs_c:
        self.copies_c[-1].append(os.dup(input_c))
        #Copy for recover in __exit__
        os.dup2(writer_c, input_c)
        #Replace the fd with the pipe

      self.copies_py.append([])
      for input_module, input_name in zip(inputs_py_module, inputs_py_name):
        self.copies_py[-1].append(getattr(input_module, input_name))
         #Copy for recover in __exit__
        setattr(input_module, input_name, writer_fid)
        #Replace the File with the pipe File object

    return self

  def __exit__(self, exc_type=None, exc_value=None, traceback=None):
    ''' exit function for with statement.

        Restores stdout and stderr, closes write pipe and joins with the
        threads

        Parameters
        ----------
        exc_type : str
            The Exception Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    '''
    #Flush
    self.flush()

    for writer_fid, inputs_c, inputs_py_module, \
        inputs_py_name, copies_c, copies_py in \
        zip(self.writer_fids, self.inputs_c, self.inputs_py_module,
            self.inputs_py_name, self.copies_c, self.copies_py):
      writer_fid.close()

      for input_module, input_name, copy_py in \
          reversed(zip(inputs_py_module, inputs_py_name, copies_py)):
        setattr(input_module, input_name, copy_py)

      for input_c, copy_c in reversed(zip(inputs_c, copies_c)):
        os.dup2(copy_c, input_c)


    for copy_c in set(sum(self.copies_c, [])):
      os.close(copy_c)

    for tid in self.tids:
      tid.join()

  def startMonitor(self, readPipe, output):
    ''' Parameters
        ----------
        readPipe : int
            File descriptor number of the read pipe (from by os.pipe)
        output : int
            The xth buffer to store the string in.

        Yields
        ------
        array_like
            Appends the thread object to self.tids

    '''
    self.tids.append(threading.Thread(target=self.__bleed, args=(readPipe, output)))
    self.tids[-1].start()

  def __bleed(self, fid, output):
    ''' Parameters
        ----------
        fid : int
            The File Descriptor
        output : str
            A chunk of the file
    '''
    chunk = True

    fid_py = os.fdopen(fid)

    while chunk: #while fid isn't closed
      chunk = fid_py.readline(64*1024) #read a chunk
      #chunk = fid_py.read(64*1024) #read a chunk
      #chunk = os.read(fid, 64*1024) #read a chunk
      output.write(chunk) #write chunk

    fid_py.close()
    #os.close(fid) #Clean up those fids now


class Capture(RedirectBase): #version 1
  ''' A class to easily redirect stdout and stderr to a sting buffer

      There are times in python when using "those kind of libraries"
      where you have to capture either stdout or stderr, and many
      situations are tricky. This class will help you, by using a with
      statement.

      Example::

          with Redirect() as rid:
            some_library.call157()

          print(rid.stdout)

      There are 4 output pipe of concern, none of which are synced with
      each other (flushed together).

      stdout and stderr from c (referred to as stdout_c, stderr_c) and
      stdout and stderr from python (referred to as stdout_py, stderr_py).

      Most python functions use the python stdout/stderr, controlled by
      sys.stdout and sys.stderr. However c embedded calls are used the
      c stdout/stderr, usually fd 1 and 2. Because of this, there are 4
      streams to consider

      Once Redirect is done, the results are stored in

      >>> self.stdout_c - Standard out output for c
      >>> self.stderr_c - Standard error output for c
      >>> self.stdout_py - Standard out output for python
      >>> self.stderr_py - Standard error output for python

      When the streams are grouped, they both contain the same data.
      In the group_out and group_err case, an addition attribute is defined

      >>> self.stdout - Standard out output
      >>> self.stderr - Standard error output

      Just to make it easier


      If you want a more complicated scenario, say you want stdout_c and
      stderr_py grouped, and stderr_c and stdout_py groups because you've
      been drinking too much. Well, this is easy to do by calling Redirect
      twice:

      Example::

          with Redirect(stdout_c=None, stderr_py=None) as rid1:
            with Redirect(stderr_c=None, stdout_py=None) as rid2:
              stuff()

      Now rid1's buffer has just stderr_c and stdout_py as one group
      stream and rid2 has stfout_c and stderr_py as one grouped stream
  '''

  def __init__(self, stdout_c=1, stderr_c=2,
                     stdout_py=sys.stdout, stderr_py=sys.stderr,
                     group=True,
                     group_outerr=True, group_out=True, group_err=True):

    self.stdout_c_fd=stdout_c
    self.stderr_c_fd=stderr_c
    self.stdout_py_fid=stdout_py
    self.stderr_py_fid=stderr_py
    self.group_outerr=group_outerr
    self.group_out=group_out
    self.group_err=group_err


    ''' Initialize the Redirect object

        Parameters
        ----------
        stdout_c : :class:`int`, optional
            The fd to be replace, usually 1 will work, but change it in case
            this is not right in your case (default: 1)
            None means to not redirect
        stderr_c : :class:`int`, optional
            The fd to be replaced, usually 2 will work, but change it in case
            this is not right in your case (default: 2)
            None means to not redirect
        stdout_py : :term:`file-like object`, optional
            The file object to be replaced (default: sys.stdout)
            None means to not redirect
        stderr_py : :term:`file-like object`, optional
            The file object to be replaced (default: sys.stderr)
            None means to not redirect
        group : :class:`bool`, optional
            Should ANY of the stream be joined together. This overrides ALL of
            the following group options
        group_outerr : :class:`bool`, optional
            Should stdout and stderr use the a group stream or else it will
            have separate streams (default: True)
        group_out : :class:`bool`, optional
            Should stdout_c and stdout_py use the a group stream or else it
            will have separate streams (default: True)
        group_err : :class:`bool`, optional
            Should stderr_c and stderr_py use the a group stream or else it
            will have separate streams (default: True)
    '''

    if not group:
      self.group_outerr=False
      self.group_out=False
      self.group_err=False

    if self.group_outerr and (self.group_out or self.group_err): #that's an or, not an and
      #Basically if two of the group are True, All 4 go through one stream
      #I can't see wanting 3 and not the 4th separate.
      self.group_all = True
    else:
      self.group_all = False

    #Redirect scenarios
    self.std_cs = [] #list of lists of strings for the c outs associates with the pipes
    self.std_pys = [] #list of lists of strings for the python outs associates with the pipes

    # This fun piece of code sets up the "Redirect scenarios"
    if self.group_all:
      #add all together
      self.std_cs.append(['stdout', 'stderr'])
      self.std_pys.append(['stdout', 'stderr'])
    else:
      if self.group_out:
        #add stdouts together
        self.std_cs.append(['stdout'])
        self.std_pys.append(['stdout'])
      else:
        #add stdout_c
        self.std_cs.append(['stdout'])
        self.std_pys.append([])
        #add stdout_py
        self.std_cs.append([])
        self.std_pys.append(['stdout'])
      if self.group_err:
        #add stderrs together
        self.std_cs.append(['stderr'])
        self.std_pys.append(['stderr'])
      else:
        #add stderr_c
        self.std_cs.append(['stderr'])
        self.std_pys.append([])
        #add stderr_py
        self.std_cs.append([])
        self.std_pys.append(['stderr'])

    self.buffers = [bytes()] * len(self.std_cs)

    super(Capture, self).__init__()

  def __bleed(self, fid, bufferIndex):
    ''' Read all date from fid and store in buffer[bufferIndex]

        This function reads large chuncks at a time (for efficientcy and then
        appends them to the appropriate bufffer. When the write pipe is closed,
        the loop will end and then close the read pipe

        Parameters
        ----------
        fid : str
            The File Handle
        bufferIndex : int
            The Buffer Index
    '''

    chunk = True

    while chunk: #while fid isn't closed
      chunk = os.read(fid, 64*1024) #read a chunk
      self.buffers[bufferIndex] += chunk #add to the buffer
    os.close(fid) #Clearn up those fids now


  def startMonitor(self, readPipe, bufferIndex):
    ''' Start a read pipe monitoring thread

        Runs __bleed in a background thread to capture all the redirected
        output and stores the information in the self.buffers[bufferIndex].

        Appends the thread object to self.tids

        Parameters
        ----------
        readPipe : str
            File descriptor number of the read pipe (from by os.pipe)
        bufferIndex : int
            The xth buffer to store the string in.
    '''
    self.tids.append(threading.Thread(target=self.__bleed, args=(readPipe, bufferIndex)))
    self.tids[-1].start()


  def __enter__(self):
    ''' enter function for with statement.

        Switched out stderr and stdout, and starts pipe threads
    '''

    #Clear initial arrays
    self.tids = [] #List of threads running __bleed
    self.read_pipes = [] #list of read pipes
    self.write_pipes = [] #list of write pipes
    self.write_fids = [] # list of file objects for the write_pipes

    #Flush
    self.flush()

    #Copy of fds/handles to put back
    if self.stdout_c_fd is not None:
      self.stdout_c_copy = os.dup(self.stdout_c_fd)
    if self.stderr_c_fd is not None:
      self.stderr_c_copy = os.dup(self.stderr_c_fd)
    #self.copied = os.fdopen(os.dup(self.stdout_fd), 'wb') #Copy of stdout handle to put back

    for x in range(len(self.std_cs)):
      #Create the paired pipes
      (r,w) = os.pipe()
      self.read_pipes.append(r)
      self.write_pipes.append(w)
      self.write_fids.append(os.fdopen(w, 'wb'))

      self.startMonitor(r, len(self.read_pipes)-1)

      for std in self.std_cs[x]:
        if getattr(self, std+'_c_fd') is not None: #self.stdxxx_c_fd is not None
          #Replace the c fd with the write pipe
          os.dup2(w, getattr(self, std+'_c_fd'))
      for std in self.std_pys[x]:
        if getattr(self, std+'_py_fid') is not None: #self.stdxxx_py_fid is not None
          setattr(sys, std, self.write_fids[x])

    return self


  def __exit__(self, exc_type=None, exc_value=None, traceback=None):
    ''' exit function for with statement.

        Restores stdout and sterr, closes write pipe and joins with the
        threads

        Parameters
        ----------
        exc_type : str
            The Execption Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    '''
    #Flush
    self.flush()

    for x in range(len(self.std_cs)):
      self.write_fids[x].close()
      for std in self.std_pys[x]:
        if getattr(self, std+'_py_fid') is not None: #self.stdxxx_py_fid is not None
          #sys.stdxxx = self.stdxxx_py_fid
          setattr(sys, std, getattr(self, std+'_py_fid'))
      for std in self.std_cs[x]:
        if getattr(self, std+'_c_fd') is not None: #self.stdxxx_c_fd is not None
          #Restore stdxxx to original fd
          os.dup2(getattr(self, std+'_c_copy'), #self.stdxxx_c_copy
                  getattr(self, std+'_c_fd'))      #self.stdxxx_c
    # Close the copy now
    if self.stdout_c_fd is not None:
      os.close(self.stdout_c_copy)
    if self.stderr_c_fd is not None:
      os.close(self.stderr_c_copy)

    for tid in self.tids:
      tid.join()

    self.__renameBuffer()

  def __renameBuffer(self):
    ''' Take the buffers and store them in common names

        Instead of accessing self.buffers, the strings are copied to common
        names, including stdout_c, stderr_c, stdout_py, stdout_py. If
        group_out/group_err is True, then stdout/stderr is defines
        (respectively) just to make it easier
    '''
    for x in range(len(self.std_cs)):
      for std in self.std_cs[x]:
        setattr(self, std+'_c', self.buffers[x])
      for std in self.std_pys[x]:
        setattr(self, std+'_py', self.buffers[x])
    #Easier names just to make life easier
    if self.group_out:
      self.stdout = self.stdout_c
    if self.group_err:
      self.stderr = self.stderr_c

class StdRedirect(object):
  ''' Redirect stdout and stderr to a file object (must have a working fileno)

      This is very stable and safe to use. The only caveat to remember is that
      C can have unflushed data in the buffer, and it will not be captured
      after the StdRedirect with statement. This is not vulnerable to the
      deadlock that the GIL can introduce with the pipe methods that need extra
      threads.

      Primarily used in a with expression:

      >>> test = open('test.txt', 'w')
      >>> with StdRedirect(test):
      ...   boxm2_batch.print_db()

      Supports stdout and stderr, and stderr can be set to StdRedirect. STDOUT
      to use the same output as stdout
  '''

  STDOUT = -1
  def __init__(self, stdout=None, stderr=None):

    self.new_stdout = stdout
    self.new_stderr = stderr

    ''' Parameters
        ----------
        stdout : str
            Standard Output
        stderr : str
            Standard Error
    '''

    self.stdout_c_fd = 1
    self.stderr_c_fd = 2

  def __enter__(self):
    if self.new_stdout:
      self.old_stdout = os.dup(self.stdout_c_fd)
      os.dup2(self.new_stdout.fileno(), self.stdout_c_fd)

    if self.new_stderr:
      self.old_stderr = os.dup(self.stderr_c_fd)
      if self.new_stderr == StdRedirect.STDOUT and self.new_stdout is not None:
        os.dup2(self.new_stdout.fileno(), self.stderr_c_fd)
      else:
        os.dup2(self.new_stderr.fileno(), self.stderr_c_fd)

  def __exit__(self, exc_type=None, exc_value=None, traceback=None):
    ''' Parameters
        ----------
        exc_type : str
            The Execption Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    '''
    if self.new_stdout:
      os.dup2(self.old_stdout, self.stdout_c_fd)
      os.close(self.old_stdout)

    if self.new_stderr:
      os.dup2(self.old_stderr, self.stderr_c_fd)
      os.close(self.old_stderr)

if __name__=='__main__':
  with Redirect(group=False) as rid:
    RedirectTest._RedirectTest__simple()
  print('Stdout_c:', rid.stdout_c)
  print('Stderr_c:', rid.stderr_c)
  print('Stdout_py:', rid.stdout_py)
  print('Stderr_py:', rid.stderr_py)

fb7ef706610ea727d4ed4ddd335dde972bd35980
<mask token>


class PopenRedirect(FileRedirect):
    <mask token>

    def __init__(self, stdout=type('Foo', (object,), {'write': lambda x, y:
        None})(), stderr=type('Bar', (object,), {'write': lambda x, y: None})()
        ):
        """ Create PopenRedirect object

        Parameters
        ----------
        stdout : :term:`file-like object`
            Stdout File like object, must have .write method
        stderr : :term:`file-like object`
            Stderr File like object, must have .write method
    """
        self.stdout_output = stdout
        self.stderr_output = stderr
        super(PopenRedirect, self).__init__([stdout, stderr])

    @property
    def stdout(self):
        """ Returns
        -------
        str
            First object in wireless intrusion prevention system list.
    """
        return self.wids[0]

    @property
    def stderr(self):
        """ Returns
        -------
        str
            Second object in wireless intrusion prevention system list.
    """
        return self.wids[1]


class Redirect(RedirectBase):
    """ FAILED EXPERIMENT! You can use this, but I don't recommend it for
      anything that matters. There are too many situations where buffered
      output or multiple stdouts in windows cause this to not behave 100%
      right. Best to just not use it. PopenRedirect is good

      MORE FAILURES! It looks like when extending python with C, too many
      cout/flushes can result in a deadlock, I'm guess the GIL does something
      weird

      Similar to Capture class, except it redirect to file like objects

      There are times in python when using "those kind of libraries"
      where you have to capture either stdout or stderr, and many
      situations are tricky. This class will help you, by using a with
      statement.

      Example::

          from StringIO import StringIO
          stdout = StringIO()
          with Redirect(stdout=stdout):
            some_library.call157()

          stdout.seek(0,0)
          print(stdout.read())

      There are 4 output pipe of concern, none of which are synced with
      each other (flushed together).

      stdout and stderr from c (referred to as stdout_c, stderr_c) and
      stdout and stderr from python (referred to as stdout_py, stderr_py).

      Most python functions use the python stdout/stderr, controlled by
      sys.stdout and sys.stderr. However c embedded calls are used the
      c stdout/stderr, usually fd 1 and 2. Because of this, there are 4
      streams to consider

      Known bugs: In windows, after stdout_c is done being redirected, stdout
      buffering appears to no longer be disabled in interactive mode. What this
      means is typing

      >>> print(123)

      No longer returns 123 right away, without a

      >>> sys.stdout.flush()

      Why? I'm guessing os.dup2 breaks the buffered mode. The fix is to

      >>> sys.stdout = os.fdopen(sys.__stdout__.fileno(), 'w', 0)

      However this can only be done once or twice, and then starts failing.

      Work around is to redirect stdout when calling the command.
      For example 'python | find /v ""' Mostly works.

      Basically don't do this in Windows Command line
  """

    def __init__(self, all=None, stdout=None, stderr=None, c=None, py=None,
        stdout_c=None, stderr_c=None, stdout_py=None, stderr_py=None,
        stdout_c_fd=1, stderr_c_fd=2, stdout_py_module=sys,
        stderr_py_module=sys, stdout_py_name='stdout', stderr_py_name='stderr'
        ):
        self.stdout_c_fd = stdout_c_fd
        self.stderr_c_fd = stderr_c_fd
        self.stdout_py_module = stdout_py_module
        self.stdout_py_name = stdout_py_name
        self.stderr_py_module = stderr_py_module
        self.stderr_py_name = stderr_py_name
        self.stdout_c_out = stdout_c
        self.stderr_c_out = stderr_c
        self.stdout_py_out = stdout_py
        self.stderr_py_out = stderr_py
        """ Create the Redirect object

        Parameters
        ----------
        file_like : :term:`file-like object`, optional
            File Output Argument. All File output arguments should use File
            like Python objects that have a .write call. Many of the arguments
            override the other argument for ease of use
        all : :class:`str`, optional
            Output stdout_c, stderr_c, stdout_py and stderr_py to the all file.
        stdout : :class:`str`, optional
            Output stdout_c and stdout_py to the stdout file.
        stderr : :class:`str`, optional
            Output stderr_c and stderr_py to the stderr file.
        c : :class:`str`, optional
            Output stdout_c and stderr_c to the c file.
        py : :class:`str`, optional
            Output stdout_py and stderr_py to the py file.
        stdout_c : :class:`str`, optional
        stderr_c : :class:`str`, optional
        stdout_py : :term:`file-like object`, optional
        stderr_py : :term:`file-like object`, optional
            Output to each individual stream for maximum customization.
        stdout_c_fd : :class:`int`, optional
        stderr_c_fd : :class:`int`, optional
             The default file number used for stdout (1) and stderr (2). There
             should be no reason to override this
        stdout_py_module : :term:`module`, optional
        stderr_py_module : :term:`module`, optional
        stdout_py_name : :class:`str`, optional
        stderr_py_name : :class:`str`, optional
            Because of the nature of python, in order to replace and restore
            the python object, the module and name of attribute must be passed
            through, where name is a string and module and the actual module.
            The default is sys module and "stdout" or "stderr" (Including the
            quotes). Again, there should be no real reason to override these,
            unless you are doing some IPython/colorama redirecting, or any
            other library that messes with sys.stdout/sys.stderr
    """
        if c is not None:
            self.stdout_c_out = c
            self.stderr_c_out = c
        if py is not None:
            self.stdout_py_out = py
            self.stderr_py_out = py
        if stdout is not None:
            self.stdout_c_out = stdout
            self.stdout_py_out = stdout
        if stderr is not None:
            self.stderr_c_out = stderr
            self.stderr_py_out = stderr
        if all is not None:
            self.stdout_c_out = all
            self.stderr_c_out = all
            self.stdout_py_out = all
            self.stderr_py_out = all
        outputs = set((self.stdout_c_out, self.stderr_c_out, self.
            stdout_py_out, self.stderr_py_out))
        with Try(KeyError):
            outputs.remove(None)
        self.inputs_c = []
        self.inputs_py_module = []
        self.inputs_py_name = []
        self.outputs = []
        for output in outputs:
            self.inputs_c.append([])
            self.inputs_py_module.append([])
            self.inputs_py_name.append([])
            self.outputs.append(output)
            if self.stdout_c_out == output:
                self.inputs_c[-1].append(self.stdout_c_fd)
            if self.stderr_c_out == output:
                self.inputs_c[-1].append(self.stderr_c_fd)
            if self.stdout_py_out == output:
                self.inputs_py_module[-1].append(self.stdout_py_module)
                self.inputs_py_name[-1].append(self.stdout_py_name)
            if self.stderr_py_out == output:
                self.inputs_py_module[-1].append(self.stderr_py_module)
                self.inputs_py_name[-1].append(self.stderr_py_name)
        super(Redirect, self).__init__()

    def __enter__(self):
        """ enter function for with statement.

        Switched out stderr and stdout, and starts pipe threads
    """
        self.tids = []
        self.writer_fids = []
        self.flush()
        self.copies_c = []
        self.copies_py = []
        for output, inputs_c, inputs_py_module, inputs_py_name in zip(self.
            outputs, self.inputs_c, self.inputs_py_module, self.inputs_py_name
            ):
            reader, writer_c = os.pipe()
            writer_fid = os.fdopen(writer_c, 'wb')
            self.writer_fids.append(writer_fid)
            self.startMonitor(reader, output)
            self.copies_c.append([])
            for input_c in inputs_c:
                self.copies_c[-1].append(os.dup(input_c))
                os.dup2(writer_c, input_c)
            self.copies_py.append([])
            for input_module, input_name in zip(inputs_py_module,
                inputs_py_name):
                self.copies_py[-1].append(getattr(input_module, input_name))
                setattr(input_module, input_name, writer_fid)
        return self

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ exit function for with statement.

        Restores stdout and stderr, closes write pipe and joins with the
        threads

        Parameters
        ----------
        exc_type : str
            The Exception Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        self.flush()
        for writer_fid, inputs_c, inputs_py_module, inputs_py_name, copies_c, copies_py in zip(
            self.writer_fids, self.inputs_c, self.inputs_py_module, self.
            inputs_py_name, self.copies_c, self.copies_py):
            writer_fid.close()
            for input_module, input_name, copy_py in reversed(zip(
                inputs_py_module, inputs_py_name, copies_py)):
                setattr(input_module, input_name, copy_py)
            for input_c, copy_c in reversed(zip(inputs_c, copies_c)):
                os.dup2(copy_c, input_c)
        for copy_c in set(sum(self.copies_c, [])):
            os.close(copy_c)
        for tid in self.tids:
            tid.join()

    def startMonitor(self, readPipe, output):
        """ Parameters
        ----------
        readPipe : int
            File descriptor number of the read pipe (from by os.pipe)
        output : int
            The xth buffer to store the string in.

        Yields
        ------
        array_like
            Appends the thread object to self.tids

    """
        self.tids.append(threading.Thread(target=self.__bleed, args=(
            readPipe, output)))
        self.tids[-1].start()

    def __bleed(self, fid, output):
        """ Parameters
        ----------
        fid : int
            The File Descriptor
        output : str
            A chunk of the file
    """
        chunk = True
        fid_py = os.fdopen(fid)
        while chunk:
            chunk = fid_py.readline(64 * 1024)
            output.write(chunk)
        fid_py.close()


class Capture(RedirectBase):
    """ A class to easily redirect stdout and stderr to a sting buffer

      There are times in python when using "those kind of libraries"
      where you have to capture either stdout or stderr, and many
      situations are tricky. This class will help you, by using a with
      statement.

      Example::

          with Redirect() as rid:
            some_library.call157()

          print(rid.stdout)

      There are 4 output pipe of concern, none of which are synced with
      each other (flushed together).

      stdout and stderr from c (referred to as stdout_c, stderr_c) and
      stdout and stderr from python (referred to as stdout_py, stderr_py).

      Most python functions use the python stdout/stderr, controlled by
      sys.stdout and sys.stderr. However c embedded calls are used the
      c stdout/stderr, usually fd 1 and 2. Because of this, there are 4
      streams to consider

      Once Redirect is done, the results are stored in

      >>> self.stdout_c - Standard out output for c
      >>> self.stderr_c - Standard error output for c
      >>> self.stdout_py - Standard out output for python
      >>> self.stderr_py - Standard error output for python

      When the streams are grouped, they both contain the same data.
      In the group_out and group_err case, an addition attribute is defined

      >>> self.stdout - Standard out output
      >>> self.stderr - Standard error output

      Just to make it easier


      If you want a more complicated scenario, say you want stdout_c and
      stderr_py grouped, and stderr_c and stdout_py groups because you've
      been drinking too much. Well, this is easy to do by calling Redirect
      twice:

      Example::

          with Redirect(stdout_c=None, stderr_py=None) as rid1:
            with Redirect(stderr_c=None, stdout_py=None) as rid2:
              stuff()

      Now rid1's buffer has just stderr_c and stdout_py as one group
      stream and rid2 has stfout_c and stderr_py as one grouped stream
  """

    def __init__(self, stdout_c=1, stderr_c=2, stdout_py=sys.stdout,
        stderr_py=sys.stderr, group=True, group_outerr=True, group_out=True,
        group_err=True):
        self.stdout_c_fd = stdout_c
        self.stderr_c_fd = stderr_c
        self.stdout_py_fid = stdout_py
        self.stderr_py_fid = stderr_py
        self.group_outerr = group_outerr
        self.group_out = group_out
        self.group_err = group_err
        """ Initialize the Redirect object

        Parameters
        ----------
        stdout_c : :class:`int`, optional
            The fd to be replace, usually 1 will work, but change it in case
            this is not right in your case (default: 1)
            None means to not redirect
        stderr_c : :class:`int`, optional
            The fd to be replaced, usually 2 will work, but change it in case
            this is not right in your case (default: 2)
            None means to not redirect
        stdout_py : :term:`file-like object`, optional
            The file object to be replaced (default: sys.stdout)
            None means to not redirect
        stderr_py : :term:`file-like object`, optional
            The file object to be replaced (default: sys.stderr)
            None means to not redirect
        group : :class:`bool`, optional
            Should ANY of the stream be joined together. This overrides ALL of
            the following group options
        group_outerr : :class:`bool`, optional
            Should stdout and stderr use the a group stream or else it will
            have separate streams (default: True)
        group_out : :class:`bool`, optional
            Should stdout_c and stdout_py use the a group stream or else it
            will have separate streams (default: True)
        group_err : :class:`bool`, optional
            Should stderr_c and stderr_py use the a group stream or else it
            will have separate streams (default: True)
    """
        if not group:
            self.group_outerr = False
            self.group_out = False
            self.group_err = False
        if self.group_outerr and (self.group_out or self.group_err):
            self.group_all = True
        else:
            self.group_all = False
        self.std_cs = []
        self.std_pys = []
        if self.group_all:
            self.std_cs.append(['stdout', 'stderr'])
            self.std_pys.append(['stdout', 'stderr'])
        else:
            if self.group_out:
                self.std_cs.append(['stdout'])
                self.std_pys.append(['stdout'])
            else:
                self.std_cs.append(['stdout'])
                self.std_pys.append([])
                self.std_cs.append([])
                self.std_pys.append(['stdout'])
            if self.group_err:
                self.std_cs.append(['stderr'])
                self.std_pys.append(['stderr'])
            else:
                self.std_cs.append(['stderr'])
                self.std_pys.append([])
                self.std_cs.append([])
                self.std_pys.append(['stderr'])
        self.buffers = [bytes()] * len(self.std_cs)
        super(Capture, self).__init__()

    def __bleed(self, fid, bufferIndex):
        """ Read all date from fid and store in buffer[bufferIndex]

        This function reads large chuncks at a time (for efficientcy and then
        appends them to the appropriate bufffer. When the write pipe is closed,
        the loop will end and then close the read pipe

        Parameters
        ----------
        fid : str
            The File Handle
        bufferIndex : int
            The Buffer Index
    """
        chunk = True
        while chunk:
            chunk = os.read(fid, 64 * 1024)
            self.buffers[bufferIndex] += chunk
        os.close(fid)

    def startMonitor(self, readPipe, bufferIndex):
        """ Start a read pipe monitoring thread

        Runs __bleed in a background thread to capture all the redirected
        output and stores the information in the self.buffers[bufferIndex].

        Appends the thread object to self.tids

        Parameters
        ----------
        readPipe : str
            File descriptor number of the read pipe (from by os.pipe)
        bufferIndex : int
            The xth buffer to store the string in.
    """
        self.tids.append(threading.Thread(target=self.__bleed, args=(
            readPipe, bufferIndex)))
        self.tids[-1].start()

    def __enter__(self):
        """ enter function for with statement.

        Switched out stderr and stdout, and starts pipe threads
    """
        self.tids = []
        self.read_pipes = []
        self.write_pipes = []
        self.write_fids = []
        self.flush()
        if self.stdout_c_fd is not None:
            self.stdout_c_copy = os.dup(self.stdout_c_fd)
        if self.stderr_c_fd is not None:
            self.stderr_c_copy = os.dup(self.stderr_c_fd)
        for x in range(len(self.std_cs)):
            r, w = os.pipe()
            self.read_pipes.append(r)
            self.write_pipes.append(w)
            self.write_fids.append(os.fdopen(w, 'wb'))
            self.startMonitor(r, len(self.read_pipes) - 1)
            for std in self.std_cs[x]:
                if getattr(self, std + '_c_fd') is not None:
                    os.dup2(w, getattr(self, std + '_c_fd'))
            for std in self.std_pys[x]:
                if getattr(self, std + '_py_fid') is not None:
                    setattr(sys, std, self.write_fids[x])
        return self

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ exit function for with statement.

        Restores stdout and sterr, closes write pipe and joins with the
        threads

        Parameters
        ----------
        exc_type : str
            The Execption Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        self.flush()
        for x in range(len(self.std_cs)):
            self.write_fids[x].close()
            for std in self.std_pys[x]:
                if getattr(self, std + '_py_fid') is not None:
                    setattr(sys, std, getattr(self, std + '_py_fid'))
            for std in self.std_cs[x]:
                if getattr(self, std + '_c_fd') is not None:
                    os.dup2(getattr(self, std + '_c_copy'), getattr(self, 
                        std + '_c_fd'))
        if self.stdout_c_fd is not None:
            os.close(self.stdout_c_copy)
        if self.stderr_c_fd is not None:
            os.close(self.stderr_c_copy)
        for tid in self.tids:
            tid.join()
        self.__renameBuffer()

    def __renameBuffer(self):
        """ Take the buffers and store them in common names

        Instead of accessing self.buffers, the strings are copied to common
        names, including stdout_c, stderr_c, stdout_py, stdout_py. If
        group_out/group_err is True, then stdout/stderr is defines
        (respectively) just to make it easier
    """
        for x in range(len(self.std_cs)):
            for std in self.std_cs[x]:
                setattr(self, std + '_c', self.buffers[x])
            for std in self.std_pys[x]:
                setattr(self, std + '_py', self.buffers[x])
        if self.group_out:
            self.stdout = self.stdout_c
        if self.group_err:
            self.stderr = self.stderr_c


class StdRedirect(object):
    """ Redirect stdout and stderr to a file object (must have a working fileno)

      This is very stable and safe to use. The only caveat to remember is that
      C can have unflushed data in the buffer, and it will not be captured
      after the StdRedirect with statement. This is not vulnerable to the
      deadlock that the GIL can introduce with the pipe methods that need extra
      threads.

      Primarily used in a with expression:

      >>> test = open('test.txt', 'w')
      >>> with StdRedirect(test):
      ...   boxm2_batch.print_db()

      Supports stdout and stderr, and stderr can be set to StdRedirect. STDOUT
      to use the same output as stdout
  """
    STDOUT = -1

    def __init__(self, stdout=None, stderr=None):
        self.new_stdout = stdout
        self.new_stderr = stderr
        """ Parameters
        ----------
        stdout : str
            Standard Output
        stderr : str
            Standard Error
    """
        self.stdout_c_fd = 1
        self.stderr_c_fd = 2

    def __enter__(self):
        if self.new_stdout:
            self.old_stdout = os.dup(self.stdout_c_fd)
            os.dup2(self.new_stdout.fileno(), self.stdout_c_fd)
        if self.new_stderr:
            self.old_stderr = os.dup(self.stderr_c_fd)
            if (self.new_stderr == StdRedirect.STDOUT and self.new_stdout
                 is not None):
                os.dup2(self.new_stdout.fileno(), self.stderr_c_fd)
            else:
                os.dup2(self.new_stderr.fileno(), self.stderr_c_fd)

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ Parameters
        ----------
        exc_type : str
            The Execption Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        if self.new_stdout:
            os.dup2(self.old_stdout, self.stdout_c_fd)
            os.close(self.old_stdout)
        if self.new_stderr:
            os.dup2(self.old_stderr, self.stderr_c_fd)
            os.close(self.old_stderr)


<mask token>

<mask token>


class FileRedirect(object):
    <mask token>
    <mask token>

    def __enter__(self):
        """ Create the pipes and monitoring threads """
        self.rids = []
        self.wids = []
        self.tids = []
        for index, output in enumerate(self.outputs):
            r, w = os.pipe()
            self.rids.append(os.fdopen(r, 'rb'))
            self.wids.append(os.fdopen(w, 'wb'))
            self.startMonitor(index)
        return self

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ Closes the write ends of the pipes, and join with monitoring threads

        The read pipes are automatically closed by the monitors

        Parameters
        ----------
        exc_type : str
            The Exception Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        for wid in self.wids:
            wid.close()
        for tid in self.tids:
            tid.join()
    <mask token>

    def startMonitor(self, stream_index):
        """ Start a new thread to monitor the stream

        Should only be called once per stream_index

        Parameters
        ----------
        stream_index : int
            The Stream Index
    """
        self.tids.append(threading.Thread(target=self.__bleed, args=(
            stream_index,)))
        self.tids[-1].start()


class PopenRedirect(FileRedirect):
    """ Version FileRedirect object geared towards redirecting for Popen commands

      >>> from StringIO import StringIO
      >>> from subprocess import Popen
      >>> out = StringIO(); err = StringIO()
      >>> with PopenRedirect(out, err) as f:
      >>>   Popen(['whoami'], stdout=f.stdout, stderr=f.stderr).wait()
      >>>   Popen(['whoami', 'non user'], stdout=f.stdout, stderr=f.stderr).wait()
      >>> out.seek(0); err.seek(0)
      >>> print(out.read(), err.read())
  """

    def __init__(self, stdout=type('Foo', (object,), {'write': lambda x, y:
        None})(), stderr=type('Bar', (object,), {'write': lambda x, y: None})()
        ):
        """ Create PopenRedirect object

        Parameters
        ----------
        stdout : :term:`file-like object`
            Stdout File like object, must have .write method
        stderr : :term:`file-like object`
            Stderr File like object, must have .write method
    """
        self.stdout_output = stdout
        self.stderr_output = stderr
        super(PopenRedirect, self).__init__([stdout, stderr])

    @property
    def stdout(self):
        """ Returns
        -------
        str
            First object in wireless intrusion prevention system list.
    """
        return self.wids[0]

    @property
    def stderr(self):
        """ Returns
        -------
        str
            Second object in wireless intrusion prevention system list.
    """
        return self.wids[1]


class Redirect(RedirectBase):
    """ FAILED EXPERIMENT! You can use this, but I don't recommend it for
      anything that matters. There are too many situations where buffered
      output or multiple stdouts in windows cause this to not behave 100%
      right. Best to just not use it. PopenRedirect is good

      MORE FAILURES! It looks like when extending python with C, too many
      cout/flushes can result in a deadlock, I'm guess the GIL does something
      weird

      Similar to Capture class, except it redirect to file like objects

      There are times in python when using "those kind of libraries"
      where you have to capture either stdout or stderr, and many
      situations are tricky. This class will help you, by using a with
      statement.

      Example::

          from StringIO import StringIO
          stdout = StringIO()
          with Redirect(stdout=stdout):
            some_library.call157()

          stdout.seek(0,0)
          print(stdout.read())

      There are 4 output pipe of concern, none of which are synced with
      each other (flushed together).

      stdout and stderr from c (referred to as stdout_c, stderr_c) and
      stdout and stderr from python (referred to as stdout_py, stderr_py).

      Most python functions use the python stdout/stderr, controlled by
      sys.stdout and sys.stderr. However c embedded calls are used the
      c stdout/stderr, usually fd 1 and 2. Because of this, there are 4
      streams to consider

      Known bugs: In windows, after stdout_c is done being redirected, stdout
      buffering appears to no longer be disabled in interactive mode. What this
      means is typing

      >>> print(123)

      No longer returns 123 right away, without a

      >>> sys.stdout.flush()

      Why? I'm guessing os.dup2 breaks the buffered mode. The fix is to

      >>> sys.stdout = os.fdopen(sys.__stdout__.fileno(), 'w', 0)

      However this can only be done once or twice, and then starts failing.

      Work around is to redirect stdout when calling the command.
      For example 'python | find /v ""' Mostly works.

      Basically don't do this in Windows Command line
  """

    def __init__(self, all=None, stdout=None, stderr=None, c=None, py=None,
        stdout_c=None, stderr_c=None, stdout_py=None, stderr_py=None,
        stdout_c_fd=1, stderr_c_fd=2, stdout_py_module=sys,
        stderr_py_module=sys, stdout_py_name='stdout', stderr_py_name='stderr'
        ):
        self.stdout_c_fd = stdout_c_fd
        self.stderr_c_fd = stderr_c_fd
        self.stdout_py_module = stdout_py_module
        self.stdout_py_name = stdout_py_name
        self.stderr_py_module = stderr_py_module
        self.stderr_py_name = stderr_py_name
        self.stdout_c_out = stdout_c
        self.stderr_c_out = stderr_c
        self.stdout_py_out = stdout_py
        self.stderr_py_out = stderr_py
        """ Create the Redirect object

        Parameters
        ----------
        file_like : :term:`file-like object`, optional
            File Output Argument. All File output arguments should use File
            like Python objects that have a .write call. Many of the arguments
            override the other argument for ease of use
        all : :class:`str`, optional
            Output stdout_c, stderr_c, stdout_py and stderr_py to the all file.
        stdout : :class:`str`, optional
            Output stdout_c and stdout_py to the stdout file.
        stderr : :class:`str`, optional
            Output stderr_c and stderr_py to the stderr file.
        c : :class:`str`, optional
            Output stdout_c and stderr_c to the c file.
        py : :class:`str`, optional
            Output stdout_py and stderr_py to the py file.
        stdout_c : :class:`str`, optional
        stderr_c : :class:`str`, optional
        stdout_py : :term:`file-like object`, optional
        stderr_py : :term:`file-like object`, optional
            Output to each individual stream for maximum customization.
        stdout_c_fd : :class:`int`, optional
        stderr_c_fd : :class:`int`, optional
             The default file number used for stdout (1) and stderr (2). There
             should be no reason to override this
        stdout_py_module : :term:`module`, optional
        stderr_py_module : :term:`module`, optional
        stdout_py_name : :class:`str`, optional
        stderr_py_name : :class:`str`, optional
            Because of the nature of python, in order to replace and restore
            the python object, the module and name of attribute must be passed
            through, where name is a string and module and the actual module.
            The default is sys module and "stdout" or "stderr" (Including the
            quotes). Again, there should be no real reason to override these,
            unless you are doing some IPython/colorama redirecting, or any
            other library that messes with sys.stdout/sys.stderr
    """
        if c is not None:
            self.stdout_c_out = c
            self.stderr_c_out = c
        if py is not None:
            self.stdout_py_out = py
            self.stderr_py_out = py
        if stdout is not None:
            self.stdout_c_out = stdout
            self.stdout_py_out = stdout
        if stderr is not None:
            self.stderr_c_out = stderr
            self.stderr_py_out = stderr
        if all is not None:
            self.stdout_c_out = all
            self.stderr_c_out = all
            self.stdout_py_out = all
            self.stderr_py_out = all
        outputs = set((self.stdout_c_out, self.stderr_c_out, self.
            stdout_py_out, self.stderr_py_out))
        with Try(KeyError):
            outputs.remove(None)
        self.inputs_c = []
        self.inputs_py_module = []
        self.inputs_py_name = []
        self.outputs = []
        for output in outputs:
            self.inputs_c.append([])
            self.inputs_py_module.append([])
            self.inputs_py_name.append([])
            self.outputs.append(output)
            if self.stdout_c_out == output:
                self.inputs_c[-1].append(self.stdout_c_fd)
            if self.stderr_c_out == output:
                self.inputs_c[-1].append(self.stderr_c_fd)
            if self.stdout_py_out == output:
                self.inputs_py_module[-1].append(self.stdout_py_module)
                self.inputs_py_name[-1].append(self.stdout_py_name)
            if self.stderr_py_out == output:
                self.inputs_py_module[-1].append(self.stderr_py_module)
                self.inputs_py_name[-1].append(self.stderr_py_name)
        super(Redirect, self).__init__()

    def __enter__(self):
        """ enter function for with statement.

        Switched out stderr and stdout, and starts pipe threads
    """
        self.tids = []
        self.writer_fids = []
        self.flush()
        self.copies_c = []
        self.copies_py = []
        for output, inputs_c, inputs_py_module, inputs_py_name in zip(self.
            outputs, self.inputs_c, self.inputs_py_module, self.inputs_py_name
            ):
            reader, writer_c = os.pipe()
            writer_fid = os.fdopen(writer_c, 'wb')
            self.writer_fids.append(writer_fid)
            self.startMonitor(reader, output)
            self.copies_c.append([])
            for input_c in inputs_c:
                self.copies_c[-1].append(os.dup(input_c))
                os.dup2(writer_c, input_c)
            self.copies_py.append([])
            for input_module, input_name in zip(inputs_py_module,
                inputs_py_name):
                self.copies_py[-1].append(getattr(input_module, input_name))
                setattr(input_module, input_name, writer_fid)
        return self

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ exit function for with statement.

        Restores stdout and stderr, closes write pipe and joins with the
        threads

        Parameters
        ----------
        exc_type : str
            The Exception Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        self.flush()
        for writer_fid, inputs_c, inputs_py_module, inputs_py_name, copies_c, copies_py in zip(
            self.writer_fids, self.inputs_c, self.inputs_py_module, self.
            inputs_py_name, self.copies_c, self.copies_py):
            writer_fid.close()
            for input_module, input_name, copy_py in reversed(zip(
                inputs_py_module, inputs_py_name, copies_py)):
                setattr(input_module, input_name, copy_py)
            for input_c, copy_c in reversed(zip(inputs_c, copies_c)):
                os.dup2(copy_c, input_c)
        for copy_c in set(sum(self.copies_c, [])):
            os.close(copy_c)
        for tid in self.tids:
            tid.join()

    def startMonitor(self, readPipe, output):
        """ Parameters
        ----------
        readPipe : int
            File descriptor number of the read pipe (from by os.pipe)
        output : int
            The xth buffer to store the string in.

        Yields
        ------
        array_like
            Appends the thread object to self.tids

    """
        self.tids.append(threading.Thread(target=self.__bleed, args=(
            readPipe, output)))
        self.tids[-1].start()

    def __bleed(self, fid, output):
        """ Parameters
        ----------
        fid : int
            The File Descriptor
        output : str
            A chunk of the file
    """
        chunk = True
        fid_py = os.fdopen(fid)
        while chunk:
            chunk = fid_py.readline(64 * 1024)
            output.write(chunk)
        fid_py.close()


class Capture(RedirectBase):
    """ A class to easily redirect stdout and stderr to a sting buffer

      There are times in python when using "those kind of libraries"
      where you have to capture either stdout or stderr, and many
      situations are tricky. This class will help you, by using a with
      statement.

      Example::

          with Redirect() as rid:
            some_library.call157()

          print(rid.stdout)

      There are 4 output pipe of concern, none of which are synced with
      each other (flushed together).

      stdout and stderr from c (referred to as stdout_c, stderr_c) and
      stdout and stderr from python (referred to as stdout_py, stderr_py).

      Most python functions use the python stdout/stderr, controlled by
      sys.stdout and sys.stderr. However c embedded calls are used the
      c stdout/stderr, usually fd 1 and 2. Because of this, there are 4
      streams to consider

      Once Redirect is done, the results are stored in

      >>> self.stdout_c - Standard out output for c
      >>> self.stderr_c - Standard error output for c
      >>> self.stdout_py - Standard out output for python
      >>> self.stderr_py - Standard error output for python

      When the streams are grouped, they both contain the same data.
      In the group_out and group_err case, an addition attribute is defined

      >>> self.stdout - Standard out output
      >>> self.stderr - Standard error output

      Just to make it easier


      If you want a more complicated scenario, say you want stdout_c and
      stderr_py grouped, and stderr_c and stdout_py groups because you've
      been drinking too much. Well, this is easy to do by calling Redirect
      twice:

      Example::

          with Redirect(stdout_c=None, stderr_py=None) as rid1:
            with Redirect(stderr_c=None, stdout_py=None) as rid2:
              stuff()

      Now rid1's buffer has just stderr_c and stdout_py as one group
      stream and rid2 has stfout_c and stderr_py as one grouped stream
  """

    def __init__(self, stdout_c=1, stderr_c=2, stdout_py=sys.stdout,
        stderr_py=sys.stderr, group=True, group_outerr=True, group_out=True,
        group_err=True):
        self.stdout_c_fd = stdout_c
        self.stderr_c_fd = stderr_c
        self.stdout_py_fid = stdout_py
        self.stderr_py_fid = stderr_py
        self.group_outerr = group_outerr
        self.group_out = group_out
        self.group_err = group_err
        """ Initialize the Redirect object

        Parameters
        ----------
        stdout_c : :class:`int`, optional
            The fd to be replace, usually 1 will work, but change it in case
            this is not right in your case (default: 1)
            None means to not redirect
        stderr_c : :class:`int`, optional
            The fd to be replaced, usually 2 will work, but change it in case
            this is not right in your case (default: 2)
            None means to not redirect
        stdout_py : :term:`file-like object`, optional
            The file object to be replaced (default: sys.stdout)
            None means to not redirect
        stderr_py : :term:`file-like object`, optional
            The file object to be replaced (default: sys.stderr)
            None means to not redirect
        group : :class:`bool`, optional
            Should ANY of the stream be joined together. This overrides ALL of
            the following group options
        group_outerr : :class:`bool`, optional
            Should stdout and stderr use the a group stream or else it will
            have separate streams (default: True)
        group_out : :class:`bool`, optional
            Should stdout_c and stdout_py use the a group stream or else it
            will have separate streams (default: True)
        group_err : :class:`bool`, optional
            Should stderr_c and stderr_py use the a group stream or else it
            will have separate streams (default: True)
    """
        if not group:
            self.group_outerr = False
            self.group_out = False
            self.group_err = False
        if self.group_outerr and (self.group_out or self.group_err):
            self.group_all = True
        else:
            self.group_all = False
        self.std_cs = []
        self.std_pys = []
        if self.group_all:
            self.std_cs.append(['stdout', 'stderr'])
            self.std_pys.append(['stdout', 'stderr'])
        else:
            if self.group_out:
                self.std_cs.append(['stdout'])
                self.std_pys.append(['stdout'])
            else:
                self.std_cs.append(['stdout'])
                self.std_pys.append([])
                self.std_cs.append([])
                self.std_pys.append(['stdout'])
            if self.group_err:
                self.std_cs.append(['stderr'])
                self.std_pys.append(['stderr'])
            else:
                self.std_cs.append(['stderr'])
                self.std_pys.append([])
                self.std_cs.append([])
                self.std_pys.append(['stderr'])
        self.buffers = [bytes()] * len(self.std_cs)
        super(Capture, self).__init__()

    def __bleed(self, fid, bufferIndex):
        """ Read all date from fid and store in buffer[bufferIndex]

        This function reads large chuncks at a time (for efficientcy and then
        appends them to the appropriate bufffer. When the write pipe is closed,
        the loop will end and then close the read pipe

        Parameters
        ----------
        fid : str
            The File Handle
        bufferIndex : int
            The Buffer Index
    """
        chunk = True
        while chunk:
            chunk = os.read(fid, 64 * 1024)
            self.buffers[bufferIndex] += chunk
        os.close(fid)

    def startMonitor(self, readPipe, bufferIndex):
        """ Start a read pipe monitoring thread

        Runs __bleed in a background thread to capture all the redirected
        output and stores the information in the self.buffers[bufferIndex].

        Appends the thread object to self.tids

        Parameters
        ----------
        readPipe : str
            File descriptor number of the read pipe (from by os.pipe)
        bufferIndex : int
            The xth buffer to store the string in.
    """
        self.tids.append(threading.Thread(target=self.__bleed, args=(
            readPipe, bufferIndex)))
        self.tids[-1].start()

    def __enter__(self):
        """ enter function for with statement.

        Switched out stderr and stdout, and starts pipe threads
    """
        self.tids = []
        self.read_pipes = []
        self.write_pipes = []
        self.write_fids = []
        self.flush()
        if self.stdout_c_fd is not None:
            self.stdout_c_copy = os.dup(self.stdout_c_fd)
        if self.stderr_c_fd is not None:
            self.stderr_c_copy = os.dup(self.stderr_c_fd)
        for x in range(len(self.std_cs)):
            r, w = os.pipe()
            self.read_pipes.append(r)
            self.write_pipes.append(w)
            self.write_fids.append(os.fdopen(w, 'wb'))
            self.startMonitor(r, len(self.read_pipes) - 1)
            for std in self.std_cs[x]:
                if getattr(self, std + '_c_fd') is not None:
                    os.dup2(w, getattr(self, std + '_c_fd'))
            for std in self.std_pys[x]:
                if getattr(self, std + '_py_fid') is not None:
                    setattr(sys, std, self.write_fids[x])
        return self

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ exit function for with statement.

        Restores stdout and sterr, closes write pipe and joins with the
        threads

        Parameters
        ----------
        exc_type : str
            The Execption Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        self.flush()
        for x in range(len(self.std_cs)):
            self.write_fids[x].close()
            for std in self.std_pys[x]:
                if getattr(self, std + '_py_fid') is not None:
                    setattr(sys, std, getattr(self, std + '_py_fid'))
            for std in self.std_cs[x]:
                if getattr(self, std + '_c_fd') is not None:
                    os.dup2(getattr(self, std + '_c_copy'), getattr(self, 
                        std + '_c_fd'))
        if self.stdout_c_fd is not None:
            os.close(self.stdout_c_copy)
        if self.stderr_c_fd is not None:
            os.close(self.stderr_c_copy)
        for tid in self.tids:
            tid.join()
        self.__renameBuffer()

    def __renameBuffer(self):
        """ Take the buffers and store them in common names

        Instead of accessing self.buffers, the strings are copied to common
        names, including stdout_c, stderr_c, stdout_py, stdout_py. If
        group_out/group_err is True, then stdout/stderr is defines
        (respectively) just to make it easier
    """
        for x in range(len(self.std_cs)):
            for std in self.std_cs[x]:
                setattr(self, std + '_c', self.buffers[x])
            for std in self.std_pys[x]:
                setattr(self, std + '_py', self.buffers[x])
        if self.group_out:
            self.stdout = self.stdout_c
        if self.group_err:
            self.stderr = self.stderr_c


class StdRedirect(object):
    """ Redirect stdout and stderr to a file object (must have a working fileno)

      This is very stable and safe to use. The only caveat to remember is that
      C can have unflushed data in the buffer, and it will not be captured
      after the StdRedirect with statement. This is not vulnerable to the
      deadlock that the GIL can introduce with the pipe methods that need extra
      threads.

      Primarily used in a with expression:

      >>> test = open('test.txt', 'w')
      >>> with StdRedirect(test):
      ...   boxm2_batch.print_db()

      Supports stdout and stderr, and stderr can be set to StdRedirect. STDOUT
      to use the same output as stdout
  """
    STDOUT = -1

    def __init__(self, stdout=None, stderr=None):
        self.new_stdout = stdout
        self.new_stderr = stderr
        """ Parameters
        ----------
        stdout : str
            Standard Output
        stderr : str
            Standard Error
    """
        self.stdout_c_fd = 1
        self.stderr_c_fd = 2

    def __enter__(self):
        if self.new_stdout:
            self.old_stdout = os.dup(self.stdout_c_fd)
            os.dup2(self.new_stdout.fileno(), self.stdout_c_fd)
        if self.new_stderr:
            self.old_stderr = os.dup(self.stderr_c_fd)
            if (self.new_stderr == StdRedirect.STDOUT and self.new_stdout
                 is not None):
                os.dup2(self.new_stdout.fileno(), self.stderr_c_fd)
            else:
                os.dup2(self.new_stderr.fileno(), self.stderr_c_fd)

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ Parameters
        ----------
        exc_type : str
            The Execption Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        if self.new_stdout:
            os.dup2(self.old_stdout, self.stdout_c_fd)
            os.close(self.old_stdout)
        if self.new_stderr:
            os.dup2(self.old_stderr, self.stderr_c_fd)
            os.close(self.old_stderr)


<mask token>

<mask token>


class Logger(object):
    <mask token>

    def __init__(self, logger, lvl=logging.INFO):
        """ Create a wrapper for logger using lvl level

        Parameters
        ----------
        logger : str
            The logger
        lvl : int
            Logging Level
    """
        self.logger = logger
        self.lvl = lvl

    def write(self, str):
        """ Write method

        Parameters
        ----------
        str : str
            The String
    """
        str = str.rstrip()
        if str:
            self.logger.log(self.lvl, str)


class FileRedirect(object):
    """ Redirect a real file object to any object with .write

      Some function need a REAL file object with file number and all. This
      class wraps a pair of pipes per output and so what everytime the pipe
      is written to, output.write is called.

      Only supports one way communication. You can write to the file object
      via any valid call, and the other end of the pipe performs "readlines"
      and calls output.write() It would be possible to expand this to two way,
      but not currently needed.

      Primarily used in a with expression:

      >>> from StringIO import StringIO
      >>> s = StringIO()
      >>> with FileRedirect([s]) as f:
      >>>   f.wids[0].write('hiya')
      >>> s.seek(0); print(s.read())

      IF you call __enter__ manually, you only need to close the wids file
      objects, and the rest of the threads and file objects are cleaned up,
      of course it would be better to call __exit__ in that case.
  """

    def __init__(self, outputs=[]):
        self.outputs = outputs
        """ Create a FileRedirect

        Parameters
        ----------
        outputs : list
            list of outputs objects to write to. For every output in outputs,
            a rid in self.rids and wid in self.wids is created when call with
            with

        Yields
        ------
        list
            list of outputs objects.
    """

    def __enter__(self):
        """ Create the pipes and monitoring threads """
        self.rids = []
        self.wids = []
        self.tids = []
        for index, output in enumerate(self.outputs):
            r, w = os.pipe()
            self.rids.append(os.fdopen(r, 'rb'))
            self.wids.append(os.fdopen(w, 'wb'))
            self.startMonitor(index)
        return self

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ Closes the write ends of the pipes, and join with monitoring threads

        The read pipes are automatically closed by the monitors

        Parameters
        ----------
        exc_type : str
            The Exception Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        for wid in self.wids:
            wid.close()
        for tid in self.tids:
            tid.join()

    def __bleed(self, streamIndex):
        rid = self.rids[streamIndex]
        wid = self.wids[streamIndex]
        output = self.outputs[streamIndex]
        """ Function to read all the data and write to output

        Automactically closes read pipe when done. Can only be stopped by
        closing the write end of pipe.

        Parameters
        ----------
        streamIndex : int
            The Stream Index
    """
        chunk = True
        while chunk:
            chunk = rid.readline(64 * 1024)
            output.write(chunk)
        rid.close()

    def startMonitor(self, stream_index):
        """ Start a new thread to monitor the stream

        Should only be called once per stream_index

        Parameters
        ----------
        stream_index : int
            The Stream Index
    """
        self.tids.append(threading.Thread(target=self.__bleed, args=(
            stream_index,)))
        self.tids[-1].start()


class PopenRedirect(FileRedirect):
    """ Version FileRedirect object geared towards redirecting for Popen commands

      >>> from StringIO import StringIO
      >>> from subprocess import Popen
      >>> out = StringIO(); err = StringIO()
      >>> with PopenRedirect(out, err) as f:
      >>>   Popen(['whoami'], stdout=f.stdout, stderr=f.stderr).wait()
      >>>   Popen(['whoami', 'non user'], stdout=f.stdout, stderr=f.stderr).wait()
      >>> out.seek(0); err.seek(0)
      >>> print(out.read(), err.read())
  """

    def __init__(self, stdout=type('Foo', (object,), {'write': lambda x, y:
        None})(), stderr=type('Bar', (object,), {'write': lambda x, y: None})()
        ):
        """ Create PopenRedirect object

        Parameters
        ----------
        stdout : :term:`file-like object`
            Stdout File like object, must have .write method
        stderr : :term:`file-like object`
            Stderr File like object, must have .write method
    """
        self.stdout_output = stdout
        self.stderr_output = stderr
        super(PopenRedirect, self).__init__([stdout, stderr])

    @property
    def stdout(self):
        """ Returns
        -------
        str
            First object in wireless intrusion prevention system list.
    """
        return self.wids[0]

    @property
    def stderr(self):
        """ Returns
        -------
        str
            Second object in wireless intrusion prevention system list.
    """
        return self.wids[1]


class Redirect(RedirectBase):
    """ FAILED EXPERIMENT! You can use this, but I don't recommend it for
      anything that matters. There are too many situations where buffered
      output or multiple stdouts in windows cause this to not behave 100%
      right. Best to just not use it. PopenRedirect is good

      MORE FAILURES! It looks like when extending python with C, too many
      cout/flushes can result in a deadlock, I'm guess the GIL does something
      weird

      Similar to Capture class, except it redirect to file like objects

      There are times in python when using "those kind of libraries"
      where you have to capture either stdout or stderr, and many
      situations are tricky. This class will help you, by using a with
      statement.

      Example::

          from StringIO import StringIO
          stdout = StringIO()
          with Redirect(stdout=stdout):
            some_library.call157()

          stdout.seek(0,0)
          print(stdout.read())

      There are 4 output pipe of concern, none of which are synced with
      each other (flushed together).

      stdout and stderr from c (referred to as stdout_c, stderr_c) and
      stdout and stderr from python (referred to as stdout_py, stderr_py).

      Most python functions use the python stdout/stderr, controlled by
      sys.stdout and sys.stderr. However c embedded calls are used the
      c stdout/stderr, usually fd 1 and 2. Because of this, there are 4
      streams to consider

      Known bugs: In windows, after stdout_c is done being redirected, stdout
      buffering appears to no longer be disabled in interactive mode. What this
      means is typing

      >>> print(123)

      No longer returns 123 right away, without a

      >>> sys.stdout.flush()

      Why? I'm guessing os.dup2 breaks the buffered mode. The fix is to

      >>> sys.stdout = os.fdopen(sys.__stdout__.fileno(), 'w', 0)

      However this can only be done once or twice, and then starts failing.

      Work around is to redirect stdout when calling the command.
      For example 'python | find /v ""' Mostly works.

      Basically don't do this in Windows Command line
  """

    def __init__(self, all=None, stdout=None, stderr=None, c=None, py=None,
        stdout_c=None, stderr_c=None, stdout_py=None, stderr_py=None,
        stdout_c_fd=1, stderr_c_fd=2, stdout_py_module=sys,
        stderr_py_module=sys, stdout_py_name='stdout', stderr_py_name='stderr'
        ):
        self.stdout_c_fd = stdout_c_fd
        self.stderr_c_fd = stderr_c_fd
        self.stdout_py_module = stdout_py_module
        self.stdout_py_name = stdout_py_name
        self.stderr_py_module = stderr_py_module
        self.stderr_py_name = stderr_py_name
        self.stdout_c_out = stdout_c
        self.stderr_c_out = stderr_c
        self.stdout_py_out = stdout_py
        self.stderr_py_out = stderr_py
        """ Create the Redirect object

        Parameters
        ----------
        file_like : :term:`file-like object`, optional
            File Output Argument. All File output arguments should use File
            like Python objects that have a .write call. Many of the arguments
            override the other argument for ease of use
        all : :class:`str`, optional
            Output stdout_c, stderr_c, stdout_py and stderr_py to the all file.
        stdout : :class:`str`, optional
            Output stdout_c and stdout_py to the stdout file.
        stderr : :class:`str`, optional
            Output stderr_c and stderr_py to the stderr file.
        c : :class:`str`, optional
            Output stdout_c and stderr_c to the c file.
        py : :class:`str`, optional
            Output stdout_py and stderr_py to the py file.
        stdout_c : :class:`str`, optional
        stderr_c : :class:`str`, optional
        stdout_py : :term:`file-like object`, optional
        stderr_py : :term:`file-like object`, optional
            Output to each individual stream for maximum customization.
        stdout_c_fd : :class:`int`, optional
        stderr_c_fd : :class:`int`, optional
             The default file number used for stdout (1) and stderr (2). There
             should be no reason to override this
        stdout_py_module : :term:`module`, optional
        stderr_py_module : :term:`module`, optional
        stdout_py_name : :class:`str`, optional
        stderr_py_name : :class:`str`, optional
            Because of the nature of python, in order to replace and restore
            the python object, the module and name of attribute must be passed
            through, where name is a string and module and the actual module.
            The default is sys module and "stdout" or "stderr" (Including the
            quotes). Again, there should be no real reason to override these,
            unless you are doing some IPython/colorama redirecting, or any
            other library that messes with sys.stdout/sys.stderr
    """
        if c is not None:
            self.stdout_c_out = c
            self.stderr_c_out = c
        if py is not None:
            self.stdout_py_out = py
            self.stderr_py_out = py
        if stdout is not None:
            self.stdout_c_out = stdout
            self.stdout_py_out = stdout
        if stderr is not None:
            self.stderr_c_out = stderr
            self.stderr_py_out = stderr
        if all is not None:
            self.stdout_c_out = all
            self.stderr_c_out = all
            self.stdout_py_out = all
            self.stderr_py_out = all
        outputs = set((self.stdout_c_out, self.stderr_c_out, self.
            stdout_py_out, self.stderr_py_out))
        with Try(KeyError):
            outputs.remove(None)
        self.inputs_c = []
        self.inputs_py_module = []
        self.inputs_py_name = []
        self.outputs = []
        for output in outputs:
            self.inputs_c.append([])
            self.inputs_py_module.append([])
            self.inputs_py_name.append([])
            self.outputs.append(output)
            if self.stdout_c_out == output:
                self.inputs_c[-1].append(self.stdout_c_fd)
            if self.stderr_c_out == output:
                self.inputs_c[-1].append(self.stderr_c_fd)
            if self.stdout_py_out == output:
                self.inputs_py_module[-1].append(self.stdout_py_module)
                self.inputs_py_name[-1].append(self.stdout_py_name)
            if self.stderr_py_out == output:
                self.inputs_py_module[-1].append(self.stderr_py_module)
                self.inputs_py_name[-1].append(self.stderr_py_name)
        super(Redirect, self).__init__()

    def __enter__(self):
        """ enter function for with statement.

        Switched out stderr and stdout, and starts pipe threads
    """
        self.tids = []
        self.writer_fids = []
        self.flush()
        self.copies_c = []
        self.copies_py = []
        for output, inputs_c, inputs_py_module, inputs_py_name in zip(self.
            outputs, self.inputs_c, self.inputs_py_module, self.inputs_py_name
            ):
            reader, writer_c = os.pipe()
            writer_fid = os.fdopen(writer_c, 'wb')
            self.writer_fids.append(writer_fid)
            self.startMonitor(reader, output)
            self.copies_c.append([])
            for input_c in inputs_c:
                self.copies_c[-1].append(os.dup(input_c))
                os.dup2(writer_c, input_c)
            self.copies_py.append([])
            for input_module, input_name in zip(inputs_py_module,
                inputs_py_name):
                self.copies_py[-1].append(getattr(input_module, input_name))
                setattr(input_module, input_name, writer_fid)
        return self

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ exit function for with statement.

        Restores stdout and stderr, closes write pipe and joins with the
        threads

        Parameters
        ----------
        exc_type : str
            The Exception Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        self.flush()
        for writer_fid, inputs_c, inputs_py_module, inputs_py_name, copies_c, copies_py in zip(
            self.writer_fids, self.inputs_c, self.inputs_py_module, self.
            inputs_py_name, self.copies_c, self.copies_py):
            writer_fid.close()
            for input_module, input_name, copy_py in reversed(zip(
                inputs_py_module, inputs_py_name, copies_py)):
                setattr(input_module, input_name, copy_py)
            for input_c, copy_c in reversed(zip(inputs_c, copies_c)):
                os.dup2(copy_c, input_c)
        for copy_c in set(sum(self.copies_c, [])):
            os.close(copy_c)
        for tid in self.tids:
            tid.join()

    def startMonitor(self, readPipe, output):
        """ Parameters
        ----------
        readPipe : int
            File descriptor number of the read pipe (from by os.pipe)
        output : int
            The xth buffer to store the string in.

        Yields
        ------
        array_like
            Appends the thread object to self.tids

    """
        self.tids.append(threading.Thread(target=self.__bleed, args=(
            readPipe, output)))
        self.tids[-1].start()

    def __bleed(self, fid, output):
        """ Parameters
        ----------
        fid : int
            The File Descriptor
        output : str
            A chunk of the file
    """
        chunk = True
        fid_py = os.fdopen(fid)
        while chunk:
            chunk = fid_py.readline(64 * 1024)
            output.write(chunk)
        fid_py.close()


class Capture(RedirectBase):
    """ A class to easily redirect stdout and stderr to a sting buffer

      There are times in python when using "those kind of libraries"
      where you have to capture either stdout or stderr, and many
      situations are tricky. This class will help you, by using a with
      statement.

      Example::

          with Redirect() as rid:
            some_library.call157()

          print(rid.stdout)

      There are 4 output pipe of concern, none of which are synced with
      each other (flushed together).

      stdout and stderr from c (referred to as stdout_c, stderr_c) and
      stdout and stderr from python (referred to as stdout_py, stderr_py).

      Most python functions use the python stdout/stderr, controlled by
      sys.stdout and sys.stderr. However c embedded calls are used the
      c stdout/stderr, usually fd 1 and 2. Because of this, there are 4
      streams to consider

      Once Redirect is done, the results are stored in

      >>> self.stdout_c - Standard out output for c
      >>> self.stderr_c - Standard error output for c
      >>> self.stdout_py - Standard out output for python
      >>> self.stderr_py - Standard error output for python

      When the streams are grouped, they both contain the same data.
      In the group_out and group_err case, an addition attribute is defined

      >>> self.stdout - Standard out output
      >>> self.stderr - Standard error output

      Just to make it easier


      If you want a more complicated scenario, say you want stdout_c and
      stderr_py grouped, and stderr_c and stdout_py groups because you've
      been drinking too much. Well, this is easy to do by calling Redirect
      twice:

      Example::

          with Redirect(stdout_c=None, stderr_py=None) as rid1:
            with Redirect(stderr_c=None, stdout_py=None) as rid2:
              stuff()

      Now rid1's buffer has just stderr_c and stdout_py as one group
      stream and rid2 has stfout_c and stderr_py as one grouped stream
  """

    def __init__(self, stdout_c=1, stderr_c=2, stdout_py=sys.stdout,
        stderr_py=sys.stderr, group=True, group_outerr=True, group_out=True,
        group_err=True):
        self.stdout_c_fd = stdout_c
        self.stderr_c_fd = stderr_c
        self.stdout_py_fid = stdout_py
        self.stderr_py_fid = stderr_py
        self.group_outerr = group_outerr
        self.group_out = group_out
        self.group_err = group_err
        """ Initialize the Redirect object

        Parameters
        ----------
        stdout_c : :class:`int`, optional
            The fd to be replace, usually 1 will work, but change it in case
            this is not right in your case (default: 1)
            None means to not redirect
        stderr_c : :class:`int`, optional
            The fd to be replaced, usually 2 will work, but change it in case
            this is not right in your case (default: 2)
            None means to not redirect
        stdout_py : :term:`file-like object`, optional
            The file object to be replaced (default: sys.stdout)
            None means to not redirect
        stderr_py : :term:`file-like object`, optional
            The file object to be replaced (default: sys.stderr)
            None means to not redirect
        group : :class:`bool`, optional
            Should ANY of the stream be joined together. This overrides ALL of
            the following group options
        group_outerr : :class:`bool`, optional
            Should stdout and stderr use the a group stream or else it will
            have separate streams (default: True)
        group_out : :class:`bool`, optional
            Should stdout_c and stdout_py use the a group stream or else it
            will have separate streams (default: True)
        group_err : :class:`bool`, optional
            Should stderr_c and stderr_py use the a group stream or else it
            will have separate streams (default: True)
    """
        if not group:
            self.group_outerr = False
            self.group_out = False
            self.group_err = False
        if self.group_outerr and (self.group_out or self.group_err):
            self.group_all = True
        else:
            self.group_all = False
        self.std_cs = []
        self.std_pys = []
        if self.group_all:
            self.std_cs.append(['stdout', 'stderr'])
            self.std_pys.append(['stdout', 'stderr'])
        else:
            if self.group_out:
                self.std_cs.append(['stdout'])
                self.std_pys.append(['stdout'])
            else:
                self.std_cs.append(['stdout'])
                self.std_pys.append([])
                self.std_cs.append([])
                self.std_pys.append(['stdout'])
            if self.group_err:
                self.std_cs.append(['stderr'])
                self.std_pys.append(['stderr'])
            else:
                self.std_cs.append(['stderr'])
                self.std_pys.append([])
                self.std_cs.append([])
                self.std_pys.append(['stderr'])
        self.buffers = [bytes()] * len(self.std_cs)
        super(Capture, self).__init__()

    def __bleed(self, fid, bufferIndex):
        """ Read all date from fid and store in buffer[bufferIndex]

        This function reads large chuncks at a time (for efficientcy and then
        appends them to the appropriate bufffer. When the write pipe is closed,
        the loop will end and then close the read pipe

        Parameters
        ----------
        fid : str
            The File Handle
        bufferIndex : int
            The Buffer Index
    """
        chunk = True
        while chunk:
            chunk = os.read(fid, 64 * 1024)
            self.buffers[bufferIndex] += chunk
        os.close(fid)

    def startMonitor(self, readPipe, bufferIndex):
        """ Start a read pipe monitoring thread

        Runs __bleed in a background thread to capture all the redirected
        output and stores the information in the self.buffers[bufferIndex].

        Appends the thread object to self.tids

        Parameters
        ----------
        readPipe : str
            File descriptor number of the read pipe (from by os.pipe)
        bufferIndex : int
            The xth buffer to store the string in.
    """
        self.tids.append(threading.Thread(target=self.__bleed, args=(
            readPipe, bufferIndex)))
        self.tids[-1].start()

    def __enter__(self):
        """ enter function for with statement.

        Switched out stderr and stdout, and starts pipe threads
    """
        self.tids = []
        self.read_pipes = []
        self.write_pipes = []
        self.write_fids = []
        self.flush()
        if self.stdout_c_fd is not None:
            self.stdout_c_copy = os.dup(self.stdout_c_fd)
        if self.stderr_c_fd is not None:
            self.stderr_c_copy = os.dup(self.stderr_c_fd)
        for x in range(len(self.std_cs)):
            r, w = os.pipe()
            self.read_pipes.append(r)
            self.write_pipes.append(w)
            self.write_fids.append(os.fdopen(w, 'wb'))
            self.startMonitor(r, len(self.read_pipes) - 1)
            for std in self.std_cs[x]:
                if getattr(self, std + '_c_fd') is not None:
                    os.dup2(w, getattr(self, std + '_c_fd'))
            for std in self.std_pys[x]:
                if getattr(self, std + '_py_fid') is not None:
                    setattr(sys, std, self.write_fids[x])
        return self

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ exit function for with statement.

        Restores stdout and sterr, closes write pipe and joins with the
        threads

        Parameters
        ----------
        exc_type : str
            The Execption Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        self.flush()
        for x in range(len(self.std_cs)):
            self.write_fids[x].close()
            for std in self.std_pys[x]:
                if getattr(self, std + '_py_fid') is not None:
                    setattr(sys, std, getattr(self, std + '_py_fid'))
            for std in self.std_cs[x]:
                if getattr(self, std + '_c_fd') is not None:
                    os.dup2(getattr(self, std + '_c_copy'), getattr(self, 
                        std + '_c_fd'))
        if self.stdout_c_fd is not None:
            os.close(self.stdout_c_copy)
        if self.stderr_c_fd is not None:
            os.close(self.stderr_c_copy)
        for tid in self.tids:
            tid.join()
        self.__renameBuffer()

    def __renameBuffer(self):
        """ Take the buffers and store them in common names

        Instead of accessing self.buffers, the strings are copied to common
        names, including stdout_c, stderr_c, stdout_py, stdout_py. If
        group_out/group_err is True, then stdout/stderr is defines
        (respectively) just to make it easier
    """
        for x in range(len(self.std_cs)):
            for std in self.std_cs[x]:
                setattr(self, std + '_c', self.buffers[x])
            for std in self.std_pys[x]:
                setattr(self, std + '_py', self.buffers[x])
        if self.group_out:
            self.stdout = self.stdout_c
        if self.group_err:
            self.stderr = self.stderr_c


class StdRedirect(object):
    """ Redirect stdout and stderr to a file object (must have a working fileno)

      This is very stable and safe to use. The only caveat to remember is that
      C can have unflushed data in the buffer, and it will not be captured
      after the StdRedirect with statement. This is not vulnerable to the
      deadlock that the GIL can introduce with the pipe methods that need extra
      threads.

      Primarily used in a with expression:

      >>> test = open('test.txt', 'w')
      >>> with StdRedirect(test):
      ...   boxm2_batch.print_db()

      Supports stdout and stderr, and stderr can be set to StdRedirect. STDOUT
      to use the same output as stdout
  """
    STDOUT = -1

    def __init__(self, stdout=None, stderr=None):
        self.new_stdout = stdout
        self.new_stderr = stderr
        """ Parameters
        ----------
        stdout : str
            Standard Output
        stderr : str
            Standard Error
    """
        self.stdout_c_fd = 1
        self.stderr_c_fd = 2

    def __enter__(self):
        if self.new_stdout:
            self.old_stdout = os.dup(self.stdout_c_fd)
            os.dup2(self.new_stdout.fileno(), self.stdout_c_fd)
        if self.new_stderr:
            self.old_stderr = os.dup(self.stderr_c_fd)
            if (self.new_stderr == StdRedirect.STDOUT and self.new_stdout
                 is not None):
                os.dup2(self.new_stdout.fileno(), self.stderr_c_fd)
            else:
                os.dup2(self.new_stderr.fileno(), self.stderr_c_fd)

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ Parameters
        ----------
        exc_type : str
            The Execption Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        if self.new_stdout:
            os.dup2(self.old_stdout, self.stdout_c_fd)
            os.close(self.old_stdout)
        if self.new_stderr:
            os.dup2(self.old_stderr, self.stderr_c_fd)
            os.close(self.old_stderr)


<mask token>

<mask token>


class RedirectBase(object):

    def flush(self):
        """ Flushes stdout and stderr
    """
        sys.stdout.flush()
        sys.stderr.flush()


class Logger(object):
    """ File object wrapper for logger.

      Careful when using this, if the logging out goes to a stream that is
      redireted, you have an infinite capture loop and does not go well

      Use PopenRedirect instead of Redirect in that case
  """

    def __init__(self, logger, lvl=logging.INFO):
        """ Create a wrapper for logger using lvl level

        Parameters
        ----------
        logger : str
            The logger
        lvl : int
            Logging Level
    """
        self.logger = logger
        self.lvl = lvl

    def write(self, str):
        """ Write method

        Parameters
        ----------
        str : str
            The String
    """
        str = str.rstrip()
        if str:
            self.logger.log(self.lvl, str)


class FileRedirect(object):
    """ Redirect a real file object to any object with .write

      Some function need a REAL file object with file number and all. This
      class wraps a pair of pipes per output and so what everytime the pipe
      is written to, output.write is called.

      Only supports one way communication. You can write to the file object
      via any valid call, and the other end of the pipe performs "readlines"
      and calls output.write() It would be possible to expand this to two way,
      but not currently needed.

      Primarily used in a with expression:

      >>> from StringIO import StringIO
      >>> s = StringIO()
      >>> with FileRedirect([s]) as f:
      >>>   f.wids[0].write('hiya')
      >>> s.seek(0); print(s.read())

      IF you call __enter__ manually, you only need to close the wids file
      objects, and the rest of the threads and file objects are cleaned up,
      of course it would be better to call __exit__ in that case.
  """

    def __init__(self, outputs=[]):
        self.outputs = outputs
        """ Create a FileRedirect

        Parameters
        ----------
        outputs : list
            list of outputs objects to write to. For every output in outputs,
            a rid in self.rids and wid in self.wids is created when call with
            with

        Yields
        ------
        list
            list of outputs objects.
    """

    def __enter__(self):
        """ Create the pipes and monitoring threads """
        self.rids = []
        self.wids = []
        self.tids = []
        for index, output in enumerate(self.outputs):
            r, w = os.pipe()
            self.rids.append(os.fdopen(r, 'rb'))
            self.wids.append(os.fdopen(w, 'wb'))
            self.startMonitor(index)
        return self

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ Closes the write ends of the pipes, and join with monitoring threads

        The read pipes are automatically closed by the monitors

        Parameters
        ----------
        exc_type : str
            The Exception Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        for wid in self.wids:
            wid.close()
        for tid in self.tids:
            tid.join()

    def __bleed(self, streamIndex):
        rid = self.rids[streamIndex]
        wid = self.wids[streamIndex]
        output = self.outputs[streamIndex]
        """ Function to read all the data and write to output

        Automactically closes read pipe when done. Can only be stopped by
        closing the write end of pipe.

        Parameters
        ----------
        streamIndex : int
            The Stream Index
    """
        chunk = True
        while chunk:
            chunk = rid.readline(64 * 1024)
            output.write(chunk)
        rid.close()

    def startMonitor(self, stream_index):
        """ Start a new thread to monitor the stream

        Should only be called once per stream_index

        Parameters
        ----------
        stream_index : int
            The Stream Index
    """
        self.tids.append(threading.Thread(target=self.__bleed, args=(
            stream_index,)))
        self.tids[-1].start()


class PopenRedirect(FileRedirect):
    """ Version FileRedirect object geared towards redirecting for Popen commands

      >>> from StringIO import StringIO
      >>> from subprocess import Popen
      >>> out = StringIO(); err = StringIO()
      >>> with PopenRedirect(out, err) as f:
      >>>   Popen(['whoami'], stdout=f.stdout, stderr=f.stderr).wait()
      >>>   Popen(['whoami', 'non user'], stdout=f.stdout, stderr=f.stderr).wait()
      >>> out.seek(0); err.seek(0)
      >>> print(out.read(), err.read())
  """

    def __init__(self, stdout=type('Foo', (object,), {'write': lambda x, y:
        None})(), stderr=type('Bar', (object,), {'write': lambda x, y: None})()
        ):
        """ Create PopenRedirect object

        Parameters
        ----------
        stdout : :term:`file-like object`
            Stdout File like object, must have .write method
        stderr : :term:`file-like object`
            Stderr File like object, must have .write method
    """
        self.stdout_output = stdout
        self.stderr_output = stderr
        super(PopenRedirect, self).__init__([stdout, stderr])

    @property
    def stdout(self):
        """ Returns
        -------
        str
            First object in wireless intrusion prevention system list.
    """
        return self.wids[0]

    @property
    def stderr(self):
        """ Returns
        -------
        str
            Second object in wireless intrusion prevention system list.
    """
        return self.wids[1]


class Redirect(RedirectBase):
    """ FAILED EXPERIMENT! You can use this, but I don't recommend it for
      anything that matters. There are too many situations where buffered
      output or multiple stdouts in windows cause this to not behave 100%
      right. Best to just not use it. PopenRedirect is good

      MORE FAILURES! It looks like when extending python with C, too many
      cout/flushes can result in a deadlock, I'm guess the GIL does something
      weird

      Similar to Capture class, except it redirect to file like objects

      There are times in python when using "those kind of libraries"
      where you have to capture either stdout or stderr, and many
      situations are tricky. This class will help you, by using a with
      statement.

      Example::

          from StringIO import StringIO
          stdout = StringIO()
          with Redirect(stdout=stdout):
            some_library.call157()

          stdout.seek(0,0)
          print(stdout.read())

      There are 4 output pipe of concern, none of which are synced with
      each other (flushed together).

      stdout and stderr from c (referred to as stdout_c, stderr_c) and
      stdout and stderr from python (referred to as stdout_py, stderr_py).

      Most python functions use the python stdout/stderr, controlled by
      sys.stdout and sys.stderr. However c embedded calls are used the
      c stdout/stderr, usually fd 1 and 2. Because of this, there are 4
      streams to consider

      Known bugs: In windows, after stdout_c is done being redirected, stdout
      buffering appears to no longer be disabled in interactive mode. What this
      means is typing

      >>> print(123)

      No longer returns 123 right away, without a

      >>> sys.stdout.flush()

      Why? I'm guessing os.dup2 breaks the buffered mode. The fix is to

      >>> sys.stdout = os.fdopen(sys.__stdout__.fileno(), 'w', 0)

      However this can only be done once or twice, and then starts failing.

      Work around is to redirect stdout when calling the command.
      For example 'python | find /v ""' Mostly works.

      Basically don't do this in Windows Command line
  """

    def __init__(self, all=None, stdout=None, stderr=None, c=None, py=None,
        stdout_c=None, stderr_c=None, stdout_py=None, stderr_py=None,
        stdout_c_fd=1, stderr_c_fd=2, stdout_py_module=sys,
        stderr_py_module=sys, stdout_py_name='stdout', stderr_py_name='stderr'
        ):
        self.stdout_c_fd = stdout_c_fd
        self.stderr_c_fd = stderr_c_fd
        self.stdout_py_module = stdout_py_module
        self.stdout_py_name = stdout_py_name
        self.stderr_py_module = stderr_py_module
        self.stderr_py_name = stderr_py_name
        self.stdout_c_out = stdout_c
        self.stderr_c_out = stderr_c
        self.stdout_py_out = stdout_py
        self.stderr_py_out = stderr_py
        """ Create the Redirect object

        Parameters
        ----------
        file_like : :term:`file-like object`, optional
            File Output Argument. All File output arguments should use File
            like Python objects that have a .write call. Many of the arguments
            override the other argument for ease of use
        all : :class:`str`, optional
            Output stdout_c, stderr_c, stdout_py and stderr_py to the all file.
        stdout : :class:`str`, optional
            Output stdout_c and stdout_py to the stdout file.
        stderr : :class:`str`, optional
            Output stderr_c and stderr_py to the stderr file.
        c : :class:`str`, optional
            Output stdout_c and stderr_c to the c file.
        py : :class:`str`, optional
            Output stdout_py and stderr_py to the py file.
        stdout_c : :class:`str`, optional
        stderr_c : :class:`str`, optional
        stdout_py : :term:`file-like object`, optional
        stderr_py : :term:`file-like object`, optional
            Output to each individual stream for maximum customization.
        stdout_c_fd : :class:`int`, optional
        stderr_c_fd : :class:`int`, optional
             The default file number used for stdout (1) and stderr (2). There
             should be no reason to override this
        stdout_py_module : :term:`module`, optional
        stderr_py_module : :term:`module`, optional
        stdout_py_name : :class:`str`, optional
        stderr_py_name : :class:`str`, optional
            Because of the nature of python, in order to replace and restore
            the python object, the module and name of attribute must be passed
            through, where name is a string and module and the actual module.
            The default is sys module and "stdout" or "stderr" (Including the
            quotes). Again, there should be no real reason to override these,
            unless you are doing some IPython/colorama redirecting, or any
            other library that messes with sys.stdout/sys.stderr
    """
        if c is not None:
            self.stdout_c_out = c
            self.stderr_c_out = c
        if py is not None:
            self.stdout_py_out = py
            self.stderr_py_out = py
        if stdout is not None:
            self.stdout_c_out = stdout
            self.stdout_py_out = stdout
        if stderr is not None:
            self.stderr_c_out = stderr
            self.stderr_py_out = stderr
        if all is not None:
            self.stdout_c_out = all
            self.stderr_c_out = all
            self.stdout_py_out = all
            self.stderr_py_out = all
        outputs = set((self.stdout_c_out, self.stderr_c_out, self.
            stdout_py_out, self.stderr_py_out))
        with Try(KeyError):
            outputs.remove(None)
        self.inputs_c = []
        self.inputs_py_module = []
        self.inputs_py_name = []
        self.outputs = []
        for output in outputs:
            self.inputs_c.append([])
            self.inputs_py_module.append([])
            self.inputs_py_name.append([])
            self.outputs.append(output)
            if self.stdout_c_out == output:
                self.inputs_c[-1].append(self.stdout_c_fd)
            if self.stderr_c_out == output:
                self.inputs_c[-1].append(self.stderr_c_fd)
            if self.stdout_py_out == output:
                self.inputs_py_module[-1].append(self.stdout_py_module)
                self.inputs_py_name[-1].append(self.stdout_py_name)
            if self.stderr_py_out == output:
                self.inputs_py_module[-1].append(self.stderr_py_module)
                self.inputs_py_name[-1].append(self.stderr_py_name)
        super(Redirect, self).__init__()

    def __enter__(self):
        """ enter function for with statement.

        Switched out stderr and stdout, and starts pipe threads
    """
        self.tids = []
        self.writer_fids = []
        self.flush()
        self.copies_c = []
        self.copies_py = []
        for output, inputs_c, inputs_py_module, inputs_py_name in zip(self.
            outputs, self.inputs_c, self.inputs_py_module, self.inputs_py_name
            ):
            reader, writer_c = os.pipe()
            writer_fid = os.fdopen(writer_c, 'wb')
            self.writer_fids.append(writer_fid)
            self.startMonitor(reader, output)
            self.copies_c.append([])
            for input_c in inputs_c:
                self.copies_c[-1].append(os.dup(input_c))
                os.dup2(writer_c, input_c)
            self.copies_py.append([])
            for input_module, input_name in zip(inputs_py_module,
                inputs_py_name):
                self.copies_py[-1].append(getattr(input_module, input_name))
                setattr(input_module, input_name, writer_fid)
        return self

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ exit function for with statement.

        Restores stdout and stderr, closes write pipe and joins with the
        threads

        Parameters
        ----------
        exc_type : str
            The Exception Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        self.flush()
        for writer_fid, inputs_c, inputs_py_module, inputs_py_name, copies_c, copies_py in zip(
            self.writer_fids, self.inputs_c, self.inputs_py_module, self.
            inputs_py_name, self.copies_c, self.copies_py):
            writer_fid.close()
            for input_module, input_name, copy_py in reversed(zip(
                inputs_py_module, inputs_py_name, copies_py)):
                setattr(input_module, input_name, copy_py)
            for input_c, copy_c in reversed(zip(inputs_c, copies_c)):
                os.dup2(copy_c, input_c)
        for copy_c in set(sum(self.copies_c, [])):
            os.close(copy_c)
        for tid in self.tids:
            tid.join()

    def startMonitor(self, readPipe, output):
        """ Parameters
        ----------
        readPipe : int
            File descriptor number of the read pipe (from by os.pipe)
        output : int
            The xth buffer to store the string in.

        Yields
        ------
        array_like
            Appends the thread object to self.tids

    """
        self.tids.append(threading.Thread(target=self.__bleed, args=(
            readPipe, output)))
        self.tids[-1].start()

    def __bleed(self, fid, output):
        """ Parameters
        ----------
        fid : int
            The File Descriptor
        output : str
            A chunk of the file
    """
        chunk = True
        fid_py = os.fdopen(fid)
        while chunk:
            chunk = fid_py.readline(64 * 1024)
            output.write(chunk)
        fid_py.close()


class Capture(RedirectBase):
    """ A class to easily redirect stdout and stderr to a sting buffer

      There are times in python when using "those kind of libraries"
      where you have to capture either stdout or stderr, and many
      situations are tricky. This class will help you, by using a with
      statement.

      Example::

          with Redirect() as rid:
            some_library.call157()

          print(rid.stdout)

      There are 4 output pipe of concern, none of which are synced with
      each other (flushed together).

      stdout and stderr from c (referred to as stdout_c, stderr_c) and
      stdout and stderr from python (referred to as stdout_py, stderr_py).

      Most python functions use the python stdout/stderr, controlled by
      sys.stdout and sys.stderr. However c embedded calls are used the
      c stdout/stderr, usually fd 1 and 2. Because of this, there are 4
      streams to consider

      Once Redirect is done, the results are stored in

      >>> self.stdout_c - Standard out output for c
      >>> self.stderr_c - Standard error output for c
      >>> self.stdout_py - Standard out output for python
      >>> self.stderr_py - Standard error output for python

      When the streams are grouped, they both contain the same data.
      In the group_out and group_err case, an addition attribute is defined

      >>> self.stdout - Standard out output
      >>> self.stderr - Standard error output

      Just to make it easier


      If you want a more complicated scenario, say you want stdout_c and
      stderr_py grouped, and stderr_c and stdout_py groups because you've
      been drinking too much. Well, this is easy to do by calling Redirect
      twice:

      Example::

          with Redirect(stdout_c=None, stderr_py=None) as rid1:
            with Redirect(stderr_c=None, stdout_py=None) as rid2:
              stuff()

      Now rid1's buffer has just stderr_c and stdout_py as one group
      stream and rid2 has stfout_c and stderr_py as one grouped stream
  """

    def __init__(self, stdout_c=1, stderr_c=2, stdout_py=sys.stdout,
        stderr_py=sys.stderr, group=True, group_outerr=True, group_out=True,
        group_err=True):
        self.stdout_c_fd = stdout_c
        self.stderr_c_fd = stderr_c
        self.stdout_py_fid = stdout_py
        self.stderr_py_fid = stderr_py
        self.group_outerr = group_outerr
        self.group_out = group_out
        self.group_err = group_err
        """ Initialize the Redirect object

        Parameters
        ----------
        stdout_c : :class:`int`, optional
            The fd to be replace, usually 1 will work, but change it in case
            this is not right in your case (default: 1)
            None means to not redirect
        stderr_c : :class:`int`, optional
            The fd to be replaced, usually 2 will work, but change it in case
            this is not right in your case (default: 2)
            None means to not redirect
        stdout_py : :term:`file-like object`, optional
            The file object to be replaced (default: sys.stdout)
            None means to not redirect
        stderr_py : :term:`file-like object`, optional
            The file object to be replaced (default: sys.stderr)
            None means to not redirect
        group : :class:`bool`, optional
            Should ANY of the stream be joined together. This overrides ALL of
            the following group options
        group_outerr : :class:`bool`, optional
            Should stdout and stderr use the a group stream or else it will
            have separate streams (default: True)
        group_out : :class:`bool`, optional
            Should stdout_c and stdout_py use the a group stream or else it
            will have separate streams (default: True)
        group_err : :class:`bool`, optional
            Should stderr_c and stderr_py use the a group stream or else it
            will have separate streams (default: True)
    """
        if not group:
            self.group_outerr = False
            self.group_out = False
            self.group_err = False
        if self.group_outerr and (self.group_out or self.group_err):
            self.group_all = True
        else:
            self.group_all = False
        self.std_cs = []
        self.std_pys = []
        if self.group_all:
            self.std_cs.append(['stdout', 'stderr'])
            self.std_pys.append(['stdout', 'stderr'])
        else:
            if self.group_out:
                self.std_cs.append(['stdout'])
                self.std_pys.append(['stdout'])
            else:
                self.std_cs.append(['stdout'])
                self.std_pys.append([])
                self.std_cs.append([])
                self.std_pys.append(['stdout'])
            if self.group_err:
                self.std_cs.append(['stderr'])
                self.std_pys.append(['stderr'])
            else:
                self.std_cs.append(['stderr'])
                self.std_pys.append([])
                self.std_cs.append([])
                self.std_pys.append(['stderr'])
        self.buffers = [bytes()] * len(self.std_cs)
        super(Capture, self).__init__()

    def __bleed(self, fid, bufferIndex):
        """ Read all date from fid and store in buffer[bufferIndex]

        This function reads large chuncks at a time (for efficientcy and then
        appends them to the appropriate bufffer. When the write pipe is closed,
        the loop will end and then close the read pipe

        Parameters
        ----------
        fid : str
            The File Handle
        bufferIndex : int
            The Buffer Index
    """
        chunk = True
        while chunk:
            chunk = os.read(fid, 64 * 1024)
            self.buffers[bufferIndex] += chunk
        os.close(fid)

    def startMonitor(self, readPipe, bufferIndex):
        """ Start a read pipe monitoring thread

        Runs __bleed in a background thread to capture all the redirected
        output and stores the information in the self.buffers[bufferIndex].

        Appends the thread object to self.tids

        Parameters
        ----------
        readPipe : str
            File descriptor number of the read pipe (from by os.pipe)
        bufferIndex : int
            The xth buffer to store the string in.
    """
        self.tids.append(threading.Thread(target=self.__bleed, args=(
            readPipe, bufferIndex)))
        self.tids[-1].start()

    def __enter__(self):
        """ enter function for with statement.

        Switched out stderr and stdout, and starts pipe threads
    """
        self.tids = []
        self.read_pipes = []
        self.write_pipes = []
        self.write_fids = []
        self.flush()
        if self.stdout_c_fd is not None:
            self.stdout_c_copy = os.dup(self.stdout_c_fd)
        if self.stderr_c_fd is not None:
            self.stderr_c_copy = os.dup(self.stderr_c_fd)
        for x in range(len(self.std_cs)):
            r, w = os.pipe()
            self.read_pipes.append(r)
            self.write_pipes.append(w)
            self.write_fids.append(os.fdopen(w, 'wb'))
            self.startMonitor(r, len(self.read_pipes) - 1)
            for std in self.std_cs[x]:
                if getattr(self, std + '_c_fd') is not None:
                    os.dup2(w, getattr(self, std + '_c_fd'))
            for std in self.std_pys[x]:
                if getattr(self, std + '_py_fid') is not None:
                    setattr(sys, std, self.write_fids[x])
        return self

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ exit function for with statement.

        Restores stdout and sterr, closes write pipe and joins with the
        threads

        Parameters
        ----------
        exc_type : str
            The Execption Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        self.flush()
        for x in range(len(self.std_cs)):
            self.write_fids[x].close()
            for std in self.std_pys[x]:
                if getattr(self, std + '_py_fid') is not None:
                    setattr(sys, std, getattr(self, std + '_py_fid'))
            for std in self.std_cs[x]:
                if getattr(self, std + '_c_fd') is not None:
                    os.dup2(getattr(self, std + '_c_copy'), getattr(self, 
                        std + '_c_fd'))
        if self.stdout_c_fd is not None:
            os.close(self.stdout_c_copy)
        if self.stderr_c_fd is not None:
            os.close(self.stderr_c_copy)
        for tid in self.tids:
            tid.join()
        self.__renameBuffer()

    def __renameBuffer(self):
        """ Take the buffers and store them in common names

        Instead of accessing self.buffers, the strings are copied to common
        names, including stdout_c, stderr_c, stdout_py, stdout_py. If
        group_out/group_err is True, then stdout/stderr is defines
        (respectively) just to make it easier
    """
        for x in range(len(self.std_cs)):
            for std in self.std_cs[x]:
                setattr(self, std + '_c', self.buffers[x])
            for std in self.std_pys[x]:
                setattr(self, std + '_py', self.buffers[x])
        if self.group_out:
            self.stdout = self.stdout_c
        if self.group_err:
            self.stderr = self.stderr_c


class StdRedirect(object):
    """ Redirect stdout and stderr to a file object (must have a working fileno)

      This is very stable and safe to use. The only caveat to remember is that
      C can have unflushed data in the buffer, and it will not be captured
      after the StdRedirect with statement. This is not vulnerable to the
      deadlock that the GIL can introduce with the pipe methods that need extra
      threads.

      Primarily used in a with expression:

      >>> test = open('test.txt', 'w')
      >>> with StdRedirect(test):
      ...   boxm2_batch.print_db()

      Supports stdout and stderr, and stderr can be set to StdRedirect. STDOUT
      to use the same output as stdout
  """
    STDOUT = -1

    def __init__(self, stdout=None, stderr=None):
        self.new_stdout = stdout
        self.new_stderr = stderr
        """ Parameters
        ----------
        stdout : str
            Standard Output
        stderr : str
            Standard Error
    """
        self.stdout_c_fd = 1
        self.stderr_c_fd = 2

    def __enter__(self):
        if self.new_stdout:
            self.old_stdout = os.dup(self.stdout_c_fd)
            os.dup2(self.new_stdout.fileno(), self.stdout_c_fd)
        if self.new_stderr:
            self.old_stderr = os.dup(self.stderr_c_fd)
            if (self.new_stderr == StdRedirect.STDOUT and self.new_stdout
                 is not None):
                os.dup2(self.new_stdout.fileno(), self.stderr_c_fd)
            else:
                os.dup2(self.new_stderr.fileno(), self.stderr_c_fd)

    def __exit__(self, exc_type=None, exc_value=None, traceback=None):
        """ Parameters
        ----------
        exc_type : str
            The Execption Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    """
        if self.new_stdout:
            os.dup2(self.old_stdout, self.stdout_c_fd)
            os.close(self.old_stdout)
        if self.new_stderr:
            os.dup2(self.old_stderr, self.stderr_c_fd)
            os.close(self.old_stderr)


if __name__ == '__main__':
    with Redirect(group=False) as rid:
        RedirectTest._RedirectTest__simple()
    print('Stdout_c:', rid.stdout_c)
    print('Stderr_c:', rid.stderr_c)
    print('Stdout_py:', rid.stdout_py)
    print('Stderr_py:', rid.stderr_py)

import sys
import os
import threading
import logging

from vsi.tools.python import Try

class RedirectBase(object):
  def flush(self):
    ''' Flushes stdout and stderr
    '''
    sys.stdout.flush()
    sys.stderr.flush()

class Logger(object):
  ''' File object wrapper for logger.

      Careful when using this, if the logging out goes to a stream that is
      redireted, you have an infinite capture loop and does not go well

      Use PopenRedirect instead of Redirect in that case
  '''

  def __init__(self, logger, lvl=logging.INFO):
    ''' Create a wrapper for logger using lvl level

        Parameters
        ----------
        logger : str
            The logger
        lvl : int
            Logging Level
    '''

    self.logger=logger
    self.lvl = lvl

  def write(self, str):
    ''' Write method

        Parameters
        ----------
        str : str
            The String
    '''
    str = str.rstrip()
    if str:
      self.logger.log(self.lvl, str)

class FileRedirect(object):
  ''' Redirect a real file object to any object with .write

      Some function need a REAL file object with file number and all. This
      class wraps a pair of pipes per output and so what everytime the pipe
      is written to, output.write is called.

      Only supports one way communication. You can write to the file object
      via any valid call, and the other end of the pipe performs "readlines"
      and calls output.write() It would be possible to expand this to two way,
      but not currently needed.

      Primarily used in a with expression:

      >>> from StringIO import StringIO
      >>> s = StringIO()
      >>> with FileRedirect([s]) as f:
      >>>   f.wids[0].write('hiya')
      >>> s.seek(0); print(s.read())

      IF you call __enter__ manually, you only need to close the wids file
      objects, and the rest of the threads and file objects are cleaned up,
      of course it would be better to call __exit__ in that case.
  '''
  def __init__(self, outputs=[]):

    self.outputs = outputs
    ''' Create a FileRedirect

        Parameters
        ----------
        outputs : list
            list of outputs objects to write to. For every output in outputs,
            a rid in self.rids and wid in self.wids is created when call with
            with

        Yields
        ------
        list
            list of outputs objects.
    '''

  def __enter__(self):
    ''' Create the pipes and monitoring threads '''
    self.rids = []
    self.wids = []
    self.tids = []

    for index, output in enumerate(self.outputs):
      r, w = os.pipe()
      self.rids.append(os.fdopen(r, 'rb'))
      self.wids.append(os.fdopen(w, 'wb'))

      self.startMonitor(index)

    return self

  def __exit__(self, exc_type=None, exc_value=None, traceback=None):
    ''' Closes the write ends of the pipes, and join with monitoring threads

        The read pipes are automatically closed by the monitors

        Parameters
        ----------
        exc_type : str
            The Exception Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    '''
    for wid in self.wids:
      wid.close()

    for tid in self.tids:
      tid.join()

  def __bleed(self, streamIndex):

    rid = self.rids[streamIndex]
    wid = self.wids[streamIndex]
    output = self.outputs[streamIndex]
    ''' Function to read all the data and write to output

        Automactically closes read pipe when done. Can only be stopped by
        closing the write end of pipe.

        Parameters
        ----------
        streamIndex : int
            The Stream Index
    '''
    #do-while
    chunk = True
    while chunk: #while rid isn't closed
      chunk = rid.readline(64*1024) #read a chunk
      output.write(chunk) #write chunk
    rid.close()

  def startMonitor(self, stream_index):
    ''' Start a new thread to monitor the stream

        Should only be called once per stream_index

        Parameters
        ----------
        stream_index : int
            The Stream Index
    '''
    self.tids.append(threading.Thread(target=self.__bleed, args=(stream_index,)))
    self.tids[-1].start()

class PopenRedirect(FileRedirect):
  ''' Version FileRedirect object geared towards redirecting for Popen commands

      >>> from StringIO import StringIO
      >>> from subprocess import Popen
      >>> out = StringIO(); err = StringIO()
      >>> with PopenRedirect(out, err) as f:
      >>>   Popen(['whoami'], stdout=f.stdout, stderr=f.stderr).wait()
      >>>   Popen(['whoami', 'non user'], stdout=f.stdout, stderr=f.stderr).wait()
      >>> out.seek(0); err.seek(0)
      >>> print(out.read(), err.read())
  '''
  def __init__(self, stdout=type('Foo', (object,), {'write':lambda x,y:None})(),
                     stderr=type('Bar', (object,), {'write':lambda x,y:None})()):
    ''' Create PopenRedirect object

        Parameters
        ----------
        stdout : :term:`file-like object`
            Stdout File like object, must have .write method
        stderr : :term:`file-like object`
            Stderr File like object, must have .write method
    '''
    self.stdout_output = stdout
    self.stderr_output = stderr

    super(PopenRedirect, self).__init__([stdout, stderr])

  @property
  def stdout(self):
    ''' Returns
        -------
        str
            First object in wireless intrusion prevention system list.
    '''
    return self.wids[0]

  @property
  def stderr(self):
    ''' Returns
        -------
        str
            Second object in wireless intrusion prevention system list.
    '''
    return self.wids[1]

class Redirect(RedirectBase): #Version 2
  ''' FAILED EXPERIMENT! You can use this, but I don't recommend it for
      anything that matters. There are too many situations where buffered
      output or multiple stdouts in windows cause this to not behave 100%
      right. Best to just not use it. PopenRedirect is good

      MORE FAILURES! It looks like when extending python with C, too many
      cout/flushes can result in a deadlock, I'm guess the GIL does something
      weird

      Similar to Capture class, except it redirect to file like objects

      There are times in python when using "those kind of libraries"
      where you have to capture either stdout or stderr, and many
      situations are tricky. This class will help you, by using a with
      statement.

      Example::

          from StringIO import StringIO
          stdout = StringIO()
          with Redirect(stdout=stdout):
            some_library.call157()

          stdout.seek(0,0)
          print(stdout.read())

      There are 4 output pipe of concern, none of which are synced with
      each other (flushed together).

      stdout and stderr from c (referred to as stdout_c, stderr_c) and
      stdout and stderr from python (referred to as stdout_py, stderr_py).

      Most python functions use the python stdout/stderr, controlled by
      sys.stdout and sys.stderr. However c embedded calls are used the
      c stdout/stderr, usually fd 1 and 2. Because of this, there are 4
      streams to consider

      Known bugs: In windows, after stdout_c is done being redirected, stdout
      buffering appears to no longer be disabled in interactive mode. What this
      means is typing

      >>> print(123)

      No longer returns 123 right away, without a

      >>> sys.stdout.flush()

      Why? I'm guessing os.dup2 breaks the buffered mode. The fix is to

      >>> sys.stdout = os.fdopen(sys.__stdout__.fileno(), 'w', 0)

      However this can only be done once or twice, and then starts failing.

      Work around is to redirect stdout when calling the command.
      For example 'python | find /v ""' Mostly works.

      Basically don't do this in Windows Command line
  '''

  def __init__(self,
               all=None,
               stdout=None,
               stderr=None,
               c=None, py=None,
               stdout_c=None, stderr_c=None,
               stdout_py=None, stderr_py=None,
               stdout_c_fd=1, stderr_c_fd=2,
               stdout_py_module=sys,    stderr_py_module=sys,
               stdout_py_name='stdout', stderr_py_name='stderr'):

    #copy original file information
    self.stdout_c_fd=stdout_c_fd
    self.stderr_c_fd=stderr_c_fd
    self.stdout_py_module=stdout_py_module
    self.stdout_py_name=stdout_py_name
    self.stderr_py_module=stderr_py_module
    self.stderr_py_name=stderr_py_name

    #Determine the "who gets router who" pattern
    self.stdout_c_out = stdout_c
    self.stderr_c_out = stderr_c
    self.stdout_py_out = stdout_py
    self.stderr_py_out = stderr_py

    ''' Create the Redirect object

        Parameters
        ----------
        file_like : :term:`file-like object`, optional
            File Output Argument. All File output arguments should use File
            like Python objects that have a .write call. Many of the arguments
            override the other argument for ease of use
        all : :class:`str`, optional
            Output stdout_c, stderr_c, stdout_py and stderr_py to the all file.
        stdout : :class:`str`, optional
            Output stdout_c and stdout_py to the stdout file.
        stderr : :class:`str`, optional
            Output stderr_c and stderr_py to the stderr file.
        c : :class:`str`, optional
            Output stdout_c and stderr_c to the c file.
        py : :class:`str`, optional
            Output stdout_py and stderr_py to the py file.
        stdout_c : :class:`str`, optional
        stderr_c : :class:`str`, optional
        stdout_py : :term:`file-like object`, optional
        stderr_py : :term:`file-like object`, optional
            Output to each individual stream for maximum customization.
        stdout_c_fd : :class:`int`, optional
        stderr_c_fd : :class:`int`, optional
             The default file number used for stdout (1) and stderr (2). There
             should be no reason to override this
        stdout_py_module : :term:`module`, optional
        stderr_py_module : :term:`module`, optional
        stdout_py_name : :class:`str`, optional
        stderr_py_name : :class:`str`, optional
            Because of the nature of python, in order to replace and restore
            the python object, the module and name of attribute must be passed
            through, where name is a string and module and the actual module.
            The default is sys module and "stdout" or "stderr" (Including the
            quotes). Again, there should be no real reason to override these,
            unless you are doing some IPython/colorama redirecting, or any
            other library that messes with sys.stdout/sys.stderr
    '''

    if c is not None:
      self.stdout_c_out = c
      self.stderr_c_out = c

    if py is not None:
      self.stdout_py_out = py
      self.stderr_py_out = py

    if stdout is not None:
      self.stdout_c_out = stdout
      self.stdout_py_out = stdout

    if stderr is not None:
      self.stderr_c_out = stderr
      self.stderr_py_out = stderr

    if all is not None:
      self.stdout_c_out = all
      self.stderr_c_out = all
      self.stdout_py_out = all
      self.stderr_py_out = all

    #Determine output set
    outputs = set((self.stdout_c_out,
                   self.stderr_c_out,
                   self.stdout_py_out,
                   self.stderr_py_out))
    with Try(KeyError):
      outputs.remove(None)
    #Now output contains a unique list of outputs, not including None

    self.inputs_c = []
    self.inputs_py_module = []
    self.inputs_py_name = []
    self.outputs = []

    #Map out inputs and outputs
    for output in outputs:
      self.inputs_c.append([])
      self.inputs_py_module.append([])
      self.inputs_py_name.append([])
      self.outputs.append(output)

      if self.stdout_c_out == output:
        self.inputs_c[-1].append(self.stdout_c_fd)
      if self.stderr_c_out == output:
        self.inputs_c[-1].append(self.stderr_c_fd)
      if self.stdout_py_out == output:
        self.inputs_py_module[-1].append(self.stdout_py_module)
        self.inputs_py_name[-1].append(self.stdout_py_name)
      if self.stderr_py_out == output:
        self.inputs_py_module[-1].append(self.stderr_py_module)
        self.inputs_py_name[-1].append(self.stderr_py_name)

    super(Redirect, self).__init__()

  def __enter__(self):
    ''' enter function for with statement.

        Switched out stderr and stdout, and starts pipe threads
    '''

    #Clear initial arrays
    self.tids = [] #List of threads running __bleed
    self.writer_fids = [] # list of file objects for the write_pipes

    #Flush
    self.flush()

    self.copies_c = []
    self.copies_py = []
    for output,inputs_c, inputs_py_module, inputs_py_name in zip(self.outputs, self.inputs_c, self.inputs_py_module, self.inputs_py_name):
      #Create the paired pipes
      (reader,writer_c) = os.pipe()
      writer_fid = os.fdopen(writer_c, 'wb')
      self.writer_fids.append(writer_fid)
      #To assist in cleanup in __exit__

      self.startMonitor(reader, output)

      self.copies_c.append([])
      for input_c in inputs_c:
        self.copies_c[-1].append(os.dup(input_c))
        #Copy for recover in __exit__
        os.dup2(writer_c, input_c)
        #Replace the fd with the pipe

      self.copies_py.append([])
      for input_module, input_name in zip(inputs_py_module, inputs_py_name):
        self.copies_py[-1].append(getattr(input_module, input_name))
         #Copy for recover in __exit__
        setattr(input_module, input_name, writer_fid)
        #Replace the File with the pipe File object

    return self

  def __exit__(self, exc_type=None, exc_value=None, traceback=None):
    ''' exit function for with statement.

        Restores stdout and stderr, closes write pipe and joins with the
        threads

        Parameters
        ----------
        exc_type : str
            The Exception Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    '''
    #Flush
    self.flush()

    for writer_fid, inputs_c, inputs_py_module, \
        inputs_py_name, copies_c, copies_py in \
        zip(self.writer_fids, self.inputs_c, self.inputs_py_module,
            self.inputs_py_name, self.copies_c, self.copies_py):
      writer_fid.close()

      for input_module, input_name, copy_py in \
          reversed(zip(inputs_py_module, inputs_py_name, copies_py)):
        setattr(input_module, input_name, copy_py)

      for input_c, copy_c in reversed(zip(inputs_c, copies_c)):
        os.dup2(copy_c, input_c)


    for copy_c in set(sum(self.copies_c, [])):
      os.close(copy_c)

    for tid in self.tids:
      tid.join()

  def startMonitor(self, readPipe, output):
    ''' Parameters
        ----------
        readPipe : int
            File descriptor number of the read pipe (from by os.pipe)
        output : int
            The xth buffer to store the string in.

        Yields
        ------
        array_like
            Appends the thread object to self.tids

    '''
    self.tids.append(threading.Thread(target=self.__bleed, args=(readPipe, output)))
    self.tids[-1].start()

  def __bleed(self, fid, output):
    ''' Parameters
        ----------
        fid : int
            The File Descriptor
        output : str
            A chunk of the file
    '''
    chunk = True

    fid_py = os.fdopen(fid)

    while chunk: #while fid isn't closed
      chunk = fid_py.readline(64*1024) #read a chunk
      #chunk = fid_py.read(64*1024) #read a chunk
      #chunk = os.read(fid, 64*1024) #read a chunk
      output.write(chunk) #write chunk

    fid_py.close()
    #os.close(fid) #Clean up those fids now


class Capture(RedirectBase): #version 1
  ''' A class to easily redirect stdout and stderr to a sting buffer

      There are times in python when using "those kind of libraries"
      where you have to capture either stdout or stderr, and many
      situations are tricky. This class will help you, by using a with
      statement.

      Example::

          with Redirect() as rid:
            some_library.call157()

          print(rid.stdout)

      There are 4 output pipe of concern, none of which are synced with
      each other (flushed together).

      stdout and stderr from c (referred to as stdout_c, stderr_c) and
      stdout and stderr from python (referred to as stdout_py, stderr_py).

      Most python functions use the python stdout/stderr, controlled by
      sys.stdout and sys.stderr. However c embedded calls are used the
      c stdout/stderr, usually fd 1 and 2. Because of this, there are 4
      streams to consider

      Once Redirect is done, the results are stored in

      >>> self.stdout_c - Standard out output for c
      >>> self.stderr_c - Standard error output for c
      >>> self.stdout_py - Standard out output for python
      >>> self.stderr_py - Standard error output for python

      When the streams are grouped, they both contain the same data.
      In the group_out and group_err case, an addition attribute is defined

      >>> self.stdout - Standard out output
      >>> self.stderr - Standard error output

      Just to make it easier


      If you want a more complicated scenario, say you want stdout_c and
      stderr_py grouped, and stderr_c and stdout_py groups because you've
      been drinking too much. Well, this is easy to do by calling Redirect
      twice:

      Example::

          with Redirect(stdout_c=None, stderr_py=None) as rid1:
            with Redirect(stderr_c=None, stdout_py=None) as rid2:
              stuff()

      Now rid1's buffer has just stderr_c and stdout_py as one group
      stream and rid2 has stfout_c and stderr_py as one grouped stream
  '''

  def __init__(self, stdout_c=1, stderr_c=2,
                     stdout_py=sys.stdout, stderr_py=sys.stderr,
                     group=True,
                     group_outerr=True, group_out=True, group_err=True):

    self.stdout_c_fd=stdout_c
    self.stderr_c_fd=stderr_c
    self.stdout_py_fid=stdout_py
    self.stderr_py_fid=stderr_py
    self.group_outerr=group_outerr
    self.group_out=group_out
    self.group_err=group_err


    ''' Initialize the Redirect object

        Parameters
        ----------
        stdout_c : :class:`int`, optional
            The fd to be replace, usually 1 will work, but change it in case
            this is not right in your case (default: 1)
            None means to not redirect
        stderr_c : :class:`int`, optional
            The fd to be replaced, usually 2 will work, but change it in case
            this is not right in your case (default: 2)
            None means to not redirect
        stdout_py : :term:`file-like object`, optional
            The file object to be replaced (default: sys.stdout)
            None means to not redirect
        stderr_py : :term:`file-like object`, optional
            The file object to be replaced (default: sys.stderr)
            None means to not redirect
        group : :class:`bool`, optional
            Should ANY of the stream be joined together. This overrides ALL of
            the following group options
        group_outerr : :class:`bool`, optional
            Should stdout and stderr use the a group stream or else it will
            have separate streams (default: True)
        group_out : :class:`bool`, optional
            Should stdout_c and stdout_py use the a group stream or else it
            will have separate streams (default: True)
        group_err : :class:`bool`, optional
            Should stderr_c and stderr_py use the a group stream or else it
            will have separate streams (default: True)
    '''

    if not group:
      self.group_outerr=False
      self.group_out=False
      self.group_err=False

    if self.group_outerr and (self.group_out or self.group_err): #that's an or, not an and
      #Basically if two of the group are True, All 4 go through one stream
      #I can't see wanting 3 and not the 4th separate.
      self.group_all = True
    else:
      self.group_all = False

    #Redirect scenarios
    self.std_cs = [] #list of lists of strings for the c outs associates with the pipes
    self.std_pys = [] #list of lists of strings for the python outs associates with the pipes

    # This fun piece of code sets up the "Redirect scenarios"
    if self.group_all:
      #add all together
      self.std_cs.append(['stdout', 'stderr'])
      self.std_pys.append(['stdout', 'stderr'])
    else:
      if self.group_out:
        #add stdouts together
        self.std_cs.append(['stdout'])
        self.std_pys.append(['stdout'])
      else:
        #add stdout_c
        self.std_cs.append(['stdout'])
        self.std_pys.append([])
        #add stdout_py
        self.std_cs.append([])
        self.std_pys.append(['stdout'])
      if self.group_err:
        #add stderrs together
        self.std_cs.append(['stderr'])
        self.std_pys.append(['stderr'])
      else:
        #add stderr_c
        self.std_cs.append(['stderr'])
        self.std_pys.append([])
        #add stderr_py
        self.std_cs.append([])
        self.std_pys.append(['stderr'])

    self.buffers = [bytes()] * len(self.std_cs)

    super(Capture, self).__init__()

  def __bleed(self, fid, bufferIndex):
    ''' Read all date from fid and store in buffer[bufferIndex]

        This function reads large chuncks at a time (for efficientcy and then
        appends them to the appropriate bufffer. When the write pipe is closed,
        the loop will end and then close the read pipe

        Parameters
        ----------
        fid : str
            The File Handle
        bufferIndex : int
            The Buffer Index
    '''

    chunk = True

    while chunk: #while fid isn't closed
      chunk = os.read(fid, 64*1024) #read a chunk
      self.buffers[bufferIndex] += chunk #add to the buffer
    os.close(fid) #Clearn up those fids now


  def startMonitor(self, readPipe, bufferIndex):
    ''' Start a read pipe monitoring thread

        Runs __bleed in a background thread to capture all the redirected
        output and stores the information in the self.buffers[bufferIndex].

        Appends the thread object to self.tids

        Parameters
        ----------
        readPipe : str
            File descriptor number of the read pipe (from by os.pipe)
        bufferIndex : int
            The xth buffer to store the string in.
    '''
    self.tids.append(threading.Thread(target=self.__bleed, args=(readPipe, bufferIndex)))
    self.tids[-1].start()


  def __enter__(self):
    ''' enter function for with statement.

        Switched out stderr and stdout, and starts pipe threads
    '''

    #Clear initial arrays
    self.tids = [] #List of threads running __bleed
    self.read_pipes = [] #list of read pipes
    self.write_pipes = [] #list of write pipes
    self.write_fids = [] # list of file objects for the write_pipes

    #Flush
    self.flush()

    #Copy of fds/handles to put back
    if self.stdout_c_fd is not None:
      self.stdout_c_copy = os.dup(self.stdout_c_fd)
    if self.stderr_c_fd is not None:
      self.stderr_c_copy = os.dup(self.stderr_c_fd)
    #self.copied = os.fdopen(os.dup(self.stdout_fd), 'wb') #Copy of stdout handle to put back

    for x in range(len(self.std_cs)):
      #Create the paired pipes
      (r,w) = os.pipe()
      self.read_pipes.append(r)
      self.write_pipes.append(w)
      self.write_fids.append(os.fdopen(w, 'wb'))

      self.startMonitor(r, len(self.read_pipes)-1)

      for std in self.std_cs[x]:
        if getattr(self, std+'_c_fd') is not None: #self.stdxxx_c_fd is not None
          #Replace the c fd with the write pipe
          os.dup2(w, getattr(self, std+'_c_fd'))
      for std in self.std_pys[x]:
        if getattr(self, std+'_py_fid') is not None: #self.stdxxx_py_fid is not None
          setattr(sys, std, self.write_fids[x])

    return self


  def __exit__(self, exc_type=None, exc_value=None, traceback=None):
    ''' exit function for with statement.

        Restores stdout and sterr, closes write pipe and joins with the
        threads

        Parameters
        ----------
        exc_type : str
            The Execption Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    '''
    #Flush
    self.flush()

    for x in range(len(self.std_cs)):
      self.write_fids[x].close()
      for std in self.std_pys[x]:
        if getattr(self, std+'_py_fid') is not None: #self.stdxxx_py_fid is not None
          #sys.stdxxx = self.stdxxx_py_fid
          setattr(sys, std, getattr(self, std+'_py_fid'))
      for std in self.std_cs[x]:
        if getattr(self, std+'_c_fd') is not None: #self.stdxxx_c_fd is not None
          #Restore stdxxx to original fd
          os.dup2(getattr(self, std+'_c_copy'), #self.stdxxx_c_copy
                  getattr(self, std+'_c_fd'))      #self.stdxxx_c
    # Close the copy now
    if self.stdout_c_fd is not None:
      os.close(self.stdout_c_copy)
    if self.stderr_c_fd is not None:
      os.close(self.stderr_c_copy)

    for tid in self.tids:
      tid.join()

    self.__renameBuffer()

  def __renameBuffer(self):
    ''' Take the buffers and store them in common names

        Instead of accessing self.buffers, the strings are copied to common
        names, including stdout_c, stderr_c, stdout_py, stdout_py. If
        group_out/group_err is True, then stdout/stderr is defines
        (respectively) just to make it easier
    '''
    for x in range(len(self.std_cs)):
      for std in self.std_cs[x]:
        setattr(self, std+'_c', self.buffers[x])
      for std in self.std_pys[x]:
        setattr(self, std+'_py', self.buffers[x])
    #Easier names just to make life easier
    if self.group_out:
      self.stdout = self.stdout_c
    if self.group_err:
      self.stderr = self.stderr_c

class StdRedirect(object):
  ''' Redirect stdout and stderr to a file object (must have a working fileno)

      This is very stable and safe to use. The only caveat to remember is that
      C can have unflushed data in the buffer, and it will not be captured
      after the StdRedirect with statement. This is not vulnerable to the
      deadlock that the GIL can introduce with the pipe methods that need extra
      threads.

      Primarily used in a with expression:

      >>> test = open('test.txt', 'w')
      >>> with StdRedirect(test):
      ...   boxm2_batch.print_db()

      Supports stdout and stderr, and stderr can be set to StdRedirect. STDOUT
      to use the same output as stdout
  '''

  STDOUT = -1
  def __init__(self, stdout=None, stderr=None):

    self.new_stdout = stdout
    self.new_stderr = stderr

    ''' Parameters
        ----------
        stdout : str
            Standard Output
        stderr : str
            Standard Error
    '''

    self.stdout_c_fd = 1
    self.stderr_c_fd = 2

  def __enter__(self):
    if self.new_stdout:
      self.old_stdout = os.dup(self.stdout_c_fd)
      os.dup2(self.new_stdout.fileno(), self.stdout_c_fd)

    if self.new_stderr:
      self.old_stderr = os.dup(self.stderr_c_fd)
      if self.new_stderr == StdRedirect.STDOUT and self.new_stdout is not None:
        os.dup2(self.new_stdout.fileno(), self.stderr_c_fd)
      else:
        os.dup2(self.new_stderr.fileno(), self.stderr_c_fd)

  def __exit__(self, exc_type=None, exc_value=None, traceback=None):
    ''' Parameters
        ----------
        exc_type : str
            The Execption Type
        exc_value : float
            The Exception Value
        traceback : str
            The Traceback
    '''
    if self.new_stdout:
      os.dup2(self.old_stdout, self.stdout_c_fd)
      os.close(self.old_stdout)

    if self.new_stderr:
      os.dup2(self.old_stderr, self.stderr_c_fd)
      os.close(self.old_stderr)

if __name__=='__main__':
  with Redirect(group=False) as rid:
    RedirectTest._RedirectTest__simple()
  print('Stdout_c:', rid.stdout_c)
  print('Stderr_c:', rid.stderr_c)
  print('Stdout_py:', rid.stdout_py)
  print('Stderr_py:', rid.stderr_py)
