# Buggy code
def scp_recv(
    self, remote_file, local_file, recurse=False, copy_args=None, suffix_separator="_"
):
    """Copy remote file(s) in parallel via SCP as
    <local_file><suffix_separator><host> or as per ``copy_args`` argument.

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

    De-duplication behaviour is configurable by providing ``copy_args``
    argument, see below.

    This function, like :py:func:`ParallelSSHClient.scp_send`, 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:`pss.exceptions.SCPError` on errors copying file.
    :raises: :py:class:`OSError` on local 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`` or as per ``copy_args``
      argument if provided.
    """
    copy_args = (
        [
            {
                "local_file": suffix_separator.join([local_file, host]),
                "remote_file": remote_file,
            }
            for i, host in enumerate(self.hosts)
        ]
        if copy_args is None
        else copy_args
    )
    local_file = "%(local_file)s"
    remote_file = "%(remote_file)s"
    try:
        return [
            self.pool.spawn(
                self._scp_recv,
                host,
                remote_file % copy_args[host_i],
                local_file % copy_args[host_i],
                recurse=recurse,
            )
            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"
        )


# 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 scp_recv(
    self, remote_file, local_file, recurse=False, copy_args=None, suffix_separator="_"
):
    """Copy remote file(s) in parallel via SCP as
    <local_file><suffix_separator><host> or as per ``copy_args`` argument.

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

    De-duplication behaviour is configurable by providing ``copy_args``
    argument, see below.

    This function, like :py:func:`ParallelSSHClient.scp_send`, 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:`pss.exceptions.SCPError` on errors copying file.
    :raises: :py:class:`OSError` on local 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`` or as per ``copy_args``
      argument if provided.
    """
    copy_args = (
        [
            {
                "local_file": suffix_separator.join([local_file, host]),
                "remote_file": remote_file,
            }
            for i, host in enumerate(self.hosts)
        ]
        if copy_args is None
        else copy_args
    )
    local_file = "%(local_file)s"
    remote_file = "%(remote_file)s"
    try:
        return [
            self.pool.spawn(
                self._scp_recv,
                host_i,
                host,
                remote_file % copy_args[host_i],
                local_file % copy_args[host_i],
                recurse=recurse,
            )
            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"
        )