Download a file from one of the provided sources

        The sources will be ordered by least amount of errors, so most
        successful hosts will be tried first. In case of failure, the next
        source will be attempted, until the first successful download is
        completed or all sources have been depleted.

        :param sources: A list of dicts with 'host_name' and 'url' keys.
        :type sources: list
        :param output_directory: Directory to save the downloaded file in.
        :type output_directory: str
        :param filename: Filename assigned to the downloaded file.
        :type filename: str
        :returns: A dict with 'host_name' and 'filename' keys if the download
                  is successful, or an empty dict otherwise.
        :rtype: dict

def download(self, sources, output_directory, filename):
        """Download a file from one of the provided sources

        The sources will be ordered by least amount of errors, so most
        successful hosts will be tried first. In case of failure, the next
        source will be attempted, until the first successful download is
        completed or all sources have been depleted.

        :param sources: A list of dicts with 'host_name' and 'url' keys.
        :type sources: list
        :param output_directory: Directory to save the downloaded file in.
        :type output_directory: str
        :param filename: Filename assigned to the downloaded file.
        :type filename: str
        :returns: A dict with 'host_name' and 'filename' keys if the download
                  is successful, or an empty dict otherwise.
        :rtype: dict
        """
        valid_sources = self._filter_sources(sources)
        if not valid_sources:
            return {'error': 'no valid sources'}

        manager = Manager()
        successful_downloads = manager.list([])

        def f(source):
            if not successful_downloads:
                result = self.download_from_host(
                    source, output_directory, filename)
                if 'error' in result:
                    self._host_errors[source['host_name']] += 1
                else:
                    successful_downloads.append(result)

        multiprocessing.dummy.Pool(len(valid_sources)).map(f, valid_sources)

        return successful_downloads[0] if successful_downloads else {}