def stdio_as(stdout_fd, stderr_fd, stdin_fd):
  """Redirect sys.{stdout, stderr, stdin} to alternate file descriptors.

  As a special case, if a given destination fd is `-1`, we will replace it with an open file handle
  to `/dev/null`.

  NB: If the filehandles for sys.{stdout, stderr, stdin} have previously been closed, it's
  possible that the OS has repurposed fds `0, 1, 2` to represent other files or sockets. It's
  impossible for this method to locate all python objects which refer to those fds, so it's up
  to the caller to guarantee that `0, 1, 2` are safe to replace.

  In Python3, the streams expect unicode. To write and read bytes, access their buffer, e.g. `stdin.buffer.read()`.
  """
  with _stdio_stream_as(stdin_fd,  0, 'stdin',  'r'),\
       _stdio_stream_as(stdout_fd, 1, 'stdout', 'w'),\
       _stdio_stream_as(stderr_fd, 2, 'stderr', 'w'):
    yield
Redirect sys.{stdout, stderr, stdin} to alternate file descriptors.

  As a special case, if a given destination fd is `-1`, we will replace it with an open file handle
  to `/dev/null`.

  NB: If the filehandles for sys.{stdout, stderr, stdin} have previously been closed, it's
  possible that the OS has repurposed fds `0, 1, 2` to represent other files or sockets. It's
  impossible for this method to locate all python objects which refer to those fds, so it's up
  to the caller to guarantee that `0, 1, 2` are safe to replace.

  In Python3, the streams expect unicode. To write and read bytes, access their buffer, e.g. `stdin.buffer.read()`.