# Buggy code
def copy_remote_file(
    self,
    remote_file,
    local_file,
    recurse=False,
    suffix_separator="_",
    copy_args=None,
    **kwargs,
):
    """Copy remote file(s) in parallel as
    <local_file><suffix_separator><host>

    With a ``local_file`` value of ``myfile`` and default separator ``_``
    the resulting filename will be ``myfile_myhost`` for the file from host
    ``myhost``.

    This function, like :py:func:`ParallelSSHClient.copy_file`, returns a
    list of greenlets which can be `join`-ed on to wait for completion.

    :py:func:`gevent.joinall` function may be used to join on all greenlets
    and will also raise exceptions if called with ``raise_error=True`` -
    default is `False`.

    Alternatively call `.get` on each greenlet to raise any exceptions from
    it.

    Exceptions listed here are raised when
    either ``gevent.joinall(<greenlets>, raise_error=True)`` is called
    or ``.get`` is called on each greenlet, not this function itself.

    :param remote_file: remote filepath to copy to local host
    :type remote_file: str
    :param local_file: local filepath on local host to copy file to
    :type local_file: str
    :param recurse: whether or not to recurse
    :type recurse: bool
    :param suffix_separator: (Optional) Separator string between
      filename and host, defaults to ``_``. For example, for a
      ``local_file`` value of ``myfile`` and default separator the
      resulting filename will be ``myfile_myhost`` for the file from
      host ``myhost``. ``suffix_separator`` has no meaning if
      ``copy_args`` is provided
    :type suffix_separator: str
    :param copy_args: (Optional) Format remote_file and local_file strings
      with per-host arguments in ``copy_args``. ``copy_args`` length must
      equal length of host list -
      :py:class:`pssh.exceptions.HostArgumentException` is raised otherwise
    :type copy_args: tuple or list
    :rtype: list(:py:class:`gevent.Greenlet`) of greenlets for remote copy
      commands
    :raises: :py:class:`ValueError` when a directory is supplied to
      local_file and recurse is not set
    :raises: :py:class:`pssh.exceptions.HostArgumentException` on number of
      per-host copy arguments not equal to number of hosts
    :raises: :py:class:`IOError` on I/O errors writing files
    :raises: :py:class:`OSError` on OS errors like permission denied

    .. note ::
      Local directories in ``local_file`` that do not exist will be
      created as long as permissions allow.

    .. note ::
      File names will be de-duplicated by appending the hostname to the
      filepath separated by ``suffix_separator``.

    """
    if copy_args:
        try:
            return [
                self.pool.spawn(
                    self._copy_remote_file,
                    host,
                    remote_file % copy_args[host_i],
                    local_file % copy_args[host_i],
                    recurse=recurse,
                    **kwargs,
                )
                for host_i, host in enumerate(self.hosts)
            ]
        except IndexError:
            raise HostArgumentException(
                "Number of per-host copy arguments provided does not match "
                "number of hosts"
            )
    else:
        return [
            self.pool.spawn(
                self._copy_remote_file,
                host,
                remote_file,
                suffix_separator.join([local_file, host]),
                recurse,
                **kwargs,
            )
            for host in self.hosts
        ]


# 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 copy_remote_file(
    self,
    remote_file,
    local_file,
    recurse=False,
    suffix_separator="_",
    copy_args=None,
    **kwargs,
):
    """Copy remote file(s) in parallel as
    <local_file><suffix_separator><host>

    With a ``local_file`` value of ``myfile`` and default separator ``_``
    the resulting filename will be ``myfile_myhost`` for the file from host
    ``myhost``.

    This function, like :py:func:`ParallelSSHClient.copy_file`, returns a
    list of greenlets which can be `join`-ed on to wait for completion.

    :py:func:`gevent.joinall` function may be used to join on all greenlets
    and will also raise exceptions if called with ``raise_error=True`` -
    default is `False`.

    Alternatively call `.get` on each greenlet to raise any exceptions from
    it.

    Exceptions listed here are raised when
    either ``gevent.joinall(<greenlets>, raise_error=True)`` is called
    or ``.get`` is called on each greenlet, not this function itself.

    :param remote_file: remote filepath to copy to local host
    :type remote_file: str
    :param local_file: local filepath on local host to copy file to
    :type local_file: str
    :param recurse: whether or not to recurse
    :type recurse: bool
    :param suffix_separator: (Optional) Separator string between
      filename and host, defaults to ``_``. For example, for a
      ``local_file`` value of ``myfile`` and default separator the
      resulting filename will be ``myfile_myhost`` for the file from
      host ``myhost``. ``suffix_separator`` has no meaning if
      ``copy_args`` is provided
    :type suffix_separator: str
    :param copy_args: (Optional) Format remote_file and local_file strings
      with per-host arguments in ``copy_args``. ``copy_args`` length must
      equal length of host list -
      :py:class:`pssh.exceptions.HostArgumentException` is raised otherwise
    :type copy_args: tuple or list
    :rtype: list(:py:class:`gevent.Greenlet`) of greenlets for remote copy
      commands
    :raises: :py:class:`ValueError` when a directory is supplied to
      local_file and recurse is not set
    :raises: :py:class:`pssh.exceptions.HostArgumentException` on number of
      per-host copy arguments not equal to number of hosts
    :raises: :py:class:`IOError` on I/O errors writing files
    :raises: :py:class:`OSError` on OS errors like permission denied

    .. note ::
      Local directories in ``local_file`` that do not exist will be
      created as long as permissions allow.

    .. note ::
      File names will be de-duplicated by appending the hostname to the
      filepath separated by ``suffix_separator``.

    """
    if copy_args:
        try:
            return [
                self.pool.spawn(
                    self._copy_remote_file,
                    host_i,
                    host,
                    remote_file % copy_args[host_i],
                    local_file % copy_args[host_i],
                    recurse=recurse,
                    **kwargs,
                )
                for host_i, host in enumerate(self.hosts)
            ]
        except IndexError:
            raise HostArgumentException(
                "Number of per-host copy arguments provided does not match "
                "number of hosts"
            )
    else:
        return [
            self.pool.spawn(
                self._copy_remote_file,
                host_i,
                host,
                remote_file,
                suffix_separator.join([local_file, host]),
                recurse,
                **kwargs,
            )
            for host_i, host in enumerate(self.hosts)
        ]