# Buggy code
def run_command(
    self,
    command,
    sudo=False,
    user=None,
    stop_on_errors=True,
    shell=None,
    use_shell=True,
    use_pty=True,
    host_args=None,
    encoding="utf-8",
    **paramiko_kwargs,
):
    """Run command on all hosts in parallel, honoring self.pool_size,
    and return output buffers.

    This function will block until all commands have been received
    by remote servers and then return immediately.

    More explicitly, function will return after connection and
    authentication establishment and after commands have been received by
    successfully established SSH channels.

    Any connection and/or authentication exceptions will be raised here
    and need catching *unless* ``run_command`` is called with
    ``stop_on_errors=False`` in which case exceptions are added to host
    output instead.

    :param command: Command to run
    :type command: str
    :param sudo: (Optional) Run with sudo. Defaults to False
    :type sudo: bool
    :param user: (Optional) User to run command as. Requires sudo access
      for that user from the logged in user account.
    :type user: str
    :param stop_on_errors: (Optional) Raise exception on errors running
      command. Defaults to True. With stop_on_errors set to False,
      exceptions are instead added to output of `run_command`. See example
      usage below.
    :type stop_on_errors: bool
    :param shell: (Optional) Override shell to use to run command with.
      Defaults to login user's defined shell. Use the shell's command
      syntax, eg `shell='bash -c'` or `shell='zsh -c'`.
    :type shell: str
    :param use_shell: (Optional) Run command with or without shell. Defaults
      to True - use shell defined in user login to run command string
    :type use_shell: bool
    :param use_pty: (Optional) Enable/Disable use of pseudo terminal
      emulation. Disabling it will prohibit capturing standard input/output.
      This is required in majority of cases, exceptions being where a shell
      is not used and/or input/output is not required. In particular
      when running a command which deliberately closes input/output pipes,
      such as a daemon process, you may want to disable ``use_pty``.
      Defaults to ``True``
    :type use_pty: bool
    :param host_args: (Optional) Format command string with per-host
      arguments in ``host_args``. ``host_args`` length must equal length of
      host list - :py:class:`pssh.exceptions.HostArgumentException` is
      raised otherwise
    :type host_args: tuple or list
    :param encoding: Encoding to use for output. Must be valid
        `Python codec <https://docs.python.org/2.7/library/codecs.html>`_
    :type encoding: str
    :param paramiko_kwargs: (Optional) Extra keyword arguments to be
      passed on to :py:func:`paramiko.client.SSHClient.connect`
    :type paramiko_kwargs: dict

    :rtype: Dictionary with host as key and
      :py:class:`pssh.output.HostOutput` as value as per
      :py:func:`pssh.pssh_client.ParallelSSHClient.get_output`

    :raises: :py:class:`pssh.exceptions.AuthenticationException` on
      authentication error
    :raises: :py:class:`pssh.exceptions.UnknownHostException` on DNS
      resolution error
    :raises: :py:class:`pssh.exceptions.ConnectionErrorException` on error
      connecting
    :raises: :py:class:`pssh.exceptions.SSHException` on other undefined SSH
      errors
    :raises: :py:class:`pssh.exceptions.HostArgumentException` on number of
      host arguments not equal to number of hosts
    :raises: :py:class:`TypeError` on not enough host arguments for cmd
      string format
    :raises: :py:class:`KeyError` on no host argument key in arguments
      dict for cmd string format"""
    output = {}
    if host_args:
        try:
            cmds = [
                self.pool.spawn(
                    self._run_command,
                    host,
                    command % host_args[host_i],
                    sudo=sudo,
                    user=user,
                    shell=shell,
                    use_shell=use_shell,
                    use_pty=use_pty,
                    **paramiko_kwargs,
                )
                for host_i, host in enumerate(self.hosts)
            ]
        except IndexError:
            raise HostArgumentException(
                "Number of host arguments provided does not match number of hosts "
            )
    else:
        cmds = [
            self.pool.spawn(
                self._run_command,
                host,
                command,
                sudo=sudo,
                user=user,
                shell=shell,
                use_shell=use_shell,
                use_pty=use_pty,
                **paramiko_kwargs,
            )
            for host in self.hosts
        ]
    for cmd in cmds:
        try:
            self.get_output(cmd, output, encoding=encoding)
        except Exception:
            if stop_on_errors:
                raise
    return output


# Traceback (ValueError)
Traceback (most recent call last):
File "./aws-instances", line 172, in <module>
main()
File "./aws-instances", line 73, in main
facts = remote_facts(ips)
File "./aws-instances", line 146, in remote_facts
out_kern = client.run_command('uname -r')
File "/home/user/do-aws-ec2-facts/aws/lib64/python3.6/site-packages/pssh/clients/native/parallel.py", line 223, in run_command
greenlet_timeout=greenlet_timeout)
File "/home/user/do-aws-ec2-facts/aws/lib64/python3.6/site-packages/pssh/clients/base_pssh.py", line 96, in run_command
self.get_output(cmd, output, timeout=greenlet_timeout)
File "/home/user/do-aws-ec2-facts/aws/lib64/python3.6/site-packages/pssh/clients/base_pssh.py", line 141, in get_output
(channel, host, stdout, stderr, stdin) = cmd.get(timeout=timeout)
File "src/gevent/greenlet.py", line 709, in gevent._greenlet.Greenlet.get
File "src/gevent/greenlet.py", line 317, in gevent._greenlet.Greenlet._raise_exception
File "/home/user/do-aws-ec2-facts/aws/lib64/python3.6/site-packages/gevent/_compat.py", line 47, in reraise
raise value.with_traceback(tb)
File "src/gevent/greenlet.py", line 766, in gevent._greenlet.Greenlet.run
File "/home/user/do-aws-ec2-facts/aws/lib64/python3.6/site-packages/pssh/clients/native/parallel.py", line 237, in _run_command
raise ex
File "/home/user/do-aws-ec2-facts/aws/lib64/python3.6/site-packages/pssh/clients/native/parallel.py", line 233, in _run_command
use_pty=use_pty, encoding=encoding, timeout=timeout)
File "/home/user/do-aws-ec2-facts/aws/lib64/python3.6/site-packages/pssh/clients/native/single.py", line 442, in run_command
channel = self.execute(_command, use_pty=use_pty)
File "/home/user/do-aws-ec2-facts/aws/lib64/python3.6/site-packages/pssh/clients/native/single.py", line 324, in execute
channel = self.open_session() if channel is None else channel
File "/home/user/do-aws-ec2-facts/aws/lib64/python3.6/site-packages/pssh/clients/native/single.py", line 297, in open_session
wait_select(self.session)
File "pssh/native/_ssh2.pyx", line 171, in pssh.native._ssh2.wait_select
File "/home/user/do-aws-ec2-facts/aws/lib64/python3.6/site-packages/gevent/select.py", line 145, in select
sel_results = _original_select(rlist, wlist, xlist, 0)
ValueError: filedescriptor out of range in select()

# Fixed code
def run_command(
    self,
    command,
    sudo=False,
    user=None,
    stop_on_errors=True,
    shell=None,
    use_shell=True,
    use_pty=True,
    host_args=None,
    encoding="utf-8",
    **paramiko_kwargs,
):
    """Run command on all hosts in parallel, honoring self.pool_size,
    and return output buffers.

    This function will block until all commands have been received
    by remote servers and then return immediately.

    More explicitly, function will return after connection and
    authentication establishment and after commands have been received by
    successfully established SSH channels.

    Any connection and/or authentication exceptions will be raised here
    and need catching *unless* ``run_command`` is called with
    ``stop_on_errors=False`` in which case exceptions are added to host
    output instead.

    :param command: Command to run
    :type command: str
    :param sudo: (Optional) Run with sudo. Defaults to False
    :type sudo: bool
    :param user: (Optional) User to run command as. Requires sudo access
      for that user from the logged in user account.
    :type user: str
    :param stop_on_errors: (Optional) Raise exception on errors running
      command. Defaults to True. With stop_on_errors set to False,
      exceptions are instead added to output of `run_command`. See example
      usage below.
    :type stop_on_errors: bool
    :param shell: (Optional) Override shell to use to run command with.
      Defaults to login user's defined shell. Use the shell's command
      syntax, eg `shell='bash -c'` or `shell='zsh -c'`.
    :type shell: str
    :param use_shell: (Optional) Run command with or without shell. Defaults
      to True - use shell defined in user login to run command string
    :type use_shell: bool
    :param use_pty: (Optional) Enable/Disable use of pseudo terminal
      emulation. Disabling it will prohibit capturing standard input/output.
      This is required in majority of cases, exceptions being where a shell
      is not used and/or input/output is not required. In particular
      when running a command which deliberately closes input/output pipes,
      such as a daemon process, you may want to disable ``use_pty``.
      Defaults to ``True``
    :type use_pty: bool
    :param host_args: (Optional) Format command string with per-host
      arguments in ``host_args``. ``host_args`` length must equal length of
      host list - :py:class:`pssh.exceptions.HostArgumentException` is
      raised otherwise
    :type host_args: tuple or list
    :param encoding: Encoding to use for output. Must be valid
        `Python codec <https://docs.python.org/2.7/library/codecs.html>`_
    :type encoding: str
    :param paramiko_kwargs: (Optional) Extra keyword arguments to be
      passed on to :py:func:`paramiko.client.SSHClient.connect`
    :type paramiko_kwargs: dict

    :rtype: Dictionary with host as key and
      :py:class:`pssh.output.HostOutput` as value as per
      :py:func:`pssh.pssh_client.ParallelSSHClient.get_output`

    :raises: :py:class:`pssh.exceptions.AuthenticationException` on
      authentication error
    :raises: :py:class:`pssh.exceptions.UnknownHostException` on DNS
      resolution error
    :raises: :py:class:`pssh.exceptions.ConnectionErrorException` on error
      connecting
    :raises: :py:class:`pssh.exceptions.SSHException` on other undefined SSH
      errors
    :raises: :py:class:`pssh.exceptions.HostArgumentException` on number of
      host arguments not equal to number of hosts
    :raises: :py:class:`TypeError` on not enough host arguments for cmd
      string format
    :raises: :py:class:`KeyError` on no host argument key in arguments
      dict for cmd string format"""
    output = {}
    if host_args:
        try:
            cmds = [
                self.pool.spawn(
                    self._run_command,
                    host_i,
                    host,
                    command % host_args[host_i],
                    sudo=sudo,
                    user=user,
                    shell=shell,
                    use_shell=use_shell,
                    use_pty=use_pty,
                    **paramiko_kwargs,
                )
                for host_i, host in enumerate(self.hosts)
            ]
        except IndexError:
            raise HostArgumentException(
                "Number of host arguments provided does not match number of hosts "
            )
    else:
        cmds = [
            self.pool.spawn(
                self._run_command,
                host_i,
                host,
                command,
                sudo=sudo,
                user=user,
                shell=shell,
                use_shell=use_shell,
                use_pty=use_pty,
                **paramiko_kwargs,
            )
            for host_i, host in enumerate(self.hosts)
        ]
    for cmd in cmds:
        try:
            self.get_output(cmd, output, encoding=encoding)
        except Exception:
            if stop_on_errors:
                raise
    return output