# Buggy code
def download_collections(
    collections, output_path, apis, validate_certs, no_deps, allow_pre_release
):
    """Download Ansible collections as their tarball from a Galaxy server to the path specified and creates a requirements
    file of the downloaded requirements to be used for an install.

    :param collections: The collections to download, should be a list of tuples with (name, requirement, Galaxy Server).
    :param output_path: The path to download the collections to.
    :param apis: A list of GalaxyAPIs to query when search for a collection.
    :param validate_certs: Whether to validate the certificate if downloading a tarball from a non-Galaxy host.
    :param no_deps: Ignore any collection dependencies and only download the base requirements.
    :param allow_pre_release: Do not ignore pre-release versions when selecting the latest.
    """
    with _tempdir() as b_temp_path:
        display.display("Process install dependency map")
        with _display_progress():
            dep_map = _build_dependency_map(
                collections,
                [],
                b_temp_path,
                apis,
                validate_certs,
                True,
                True,
                no_deps,
                allow_pre_release=allow_pre_release,
            )

        requirements = []
        display.display("Starting collection download process to '%s'" % output_path)
        with _display_progress():
            for name, requirement in dep_map.items():
                collection_filename = "%s-%s-%s.tar.gz" % (
                    requirement.namespace,
                    requirement.name,
                    requirement.latest_version,
                )
                dest_path = os.path.join(output_path, collection_filename)
                requirements.append(
                    {"name": collection_filename, "version": requirement.latest_version}
                )

                display.display(
                    "Downloading collection '%s' to '%s'" % (name, dest_path)
                )

                if (
                    requirement.api is None
                    and requirement.b_path
                    and os.path.isfile(requirement.b_path)
                ):
                    shutil.copy(
                        requirement.b_path,
                        to_bytes(dest_path, errors="surrogate_or_strict"),
                    )
                elif requirement.api is None and requirement.b_path:
                    temp_path = to_text(b_temp_path, errors="surrogate_or_string")
                    scm_build_path = os.path.join(
                        temp_path, "tmp_build-%s" % collection_filename
                    )
                    os.makedirs(
                        to_bytes(scm_build_path, errors="surrogate_or_strict"),
                        mode=0o0755,
                    )
                    temp_download_path = build_collection(
                        os.path.join(temp_path, name), scm_build_path, True
                    )
                    shutil.move(
                        to_bytes(temp_download_path, errors="surrogate_or_strict"),
                        to_bytes(dest_path, errors="surrogate_or_strict"),
                    )
                else:
                    b_temp_download_path = requirement.download(b_temp_path)
                    shutil.move(
                        b_temp_download_path,
                        to_bytes(dest_path, errors="surrogate_or_strict"),
                    )

                display.display(
                    "%s (%s) was downloaded successfully"
                    % (name, requirement.latest_version)
                )

            requirements_path = os.path.join(output_path, "requirements.yml")
            display.display(
                "Writing requirements.yml file of downloaded collections to '%s'"
                % requirements_path
            )
            with open(
                to_bytes(requirements_path, errors="surrogate_or_strict"), mode="wb"
            ) as req_fd:
                req_fd.write(
                    to_bytes(
                        yaml.safe_dump({"collections": requirements}),
                        errors="surrogate_or_strict",
                    )
                )


# Traceback (AttributeError)
$ ansible-galaxy collection download -r examples/pytz/requirements.yml -p target -vvv
[WARNING]: You are running the development version of Ansible. You should only run Ansible from "devel" if you are modifying the Ansible engine, or trying out features under
development. This is a rapidly changing source of code and can become unstable at any point.
ansible-galaxy 2.11.0.dev0
config file = None
configured module search path = ['/Users/alancoding/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /Users/alancoding/Documents/repos/ansible/lib/ansible
ansible collection location = /Users/alancoding/.ansible/collections:/usr/share/ansible/collections
executable location = /Users/alancoding/.virtualenvs/ansible-builder_test/bin/ansible-galaxy
python version = 3.7.7 (default, Mar 10 2020, 15:43:03) [Clang 11.0.0 (clang-1100.0.33.17)]
No config file found; using defaults
Reading requirement file at '/Users/alancoding/Documents/repos/ansible-builder/examples/pytz/requirements.yml'
Process install dependency map
Processing requirement collection 'https://github.com/AlanCoding/awx.git#awx_collection,ee_req'
archiving ['/usr/local/bin/git', 'archive', '--prefix=awx/', '--output=/Users/alancoding/.ansible/tmp/ansible-local-72180s78cry9f/tmpxdfhelr6.tar', 'ee_req']
Skipping '/Users/alancoding/.ansible/tmp/ansible-local-72180s78cry9f/tmpj90wrb1h/awx/awx_collection/tools' for collection build
Skipping '/Users/alancoding/.ansible/tmp/ansible-local-72180s78cry9f/tmpj90wrb1h/awx/awx_collection/setup.cfg' for collection build
Skipping '/Users/alancoding/.ansible/tmp/ansible-local-72180s78cry9f/tmpj90wrb1h/awx/awx_collection/galaxy.yml' for collection build
Starting collection download process to '/Users/alancoding/Documents/repos/ansible-builder/target'
Downloading collection 'awx.awx' to '/Users/alancoding/Documents/repos/ansible-builder/target/awx-awx-0.0.1-devel.tar.gz'
ERROR! Unexpected Exception, this is probably a bug: 'NoneType' object has no attribute '_add_auth_token'
the full traceback was:

Traceback (most recent call last):
File "/Users/alancoding/Documents/repos/ansible/bin/ansible-galaxy", line 125, in <module>
exit_code = cli.run()
File "/Users/alancoding/Documents/repos/ansible/lib/ansible/cli/galaxy.py", line 498, in run
context.CLIARGS['func']()
File "/Users/alancoding/Documents/repos/ansible/lib/ansible/cli/galaxy.py", line 789, in execute_download
context.CLIARGS['allow_pre_release'])
File "/Users/alancoding/Documents/repos/ansible/lib/ansible/galaxy/collection.py", line 607, in download_collections
b_temp_download_path = requirement.download(b_temp_path)
File "/Users/alancoding/Documents/repos/ansible/lib/ansible/galaxy/collection.py", line 226, in download
self.api._add_auth_token(headers, download_url, required=False)
AttributeError: 'NoneType' object has no attribute '_add_auth_token'

# Fixed code
def download_collections(
    collections, output_path, apis, validate_certs, no_deps, allow_pre_release
):
    """Download Ansible collections as their tarball from a Galaxy server to the path specified and creates a requirements
    file of the downloaded requirements to be used for an install.

    :param collections: The collections to download, should be a list of tuples with (name, requirement, Galaxy Server).
    :param output_path: The path to download the collections to.
    :param apis: A list of GalaxyAPIs to query when search for a collection.
    :param validate_certs: Whether to validate the certificate if downloading a tarball from a non-Galaxy host.
    :param no_deps: Ignore any collection dependencies and only download the base requirements.
    :param allow_pre_release: Do not ignore pre-release versions when selecting the latest.
    """
    with _tempdir() as b_temp_path:
        display.display("Process install dependency map")
        with _display_progress():
            dep_map = _build_dependency_map(
                collections,
                [],
                b_temp_path,
                apis,
                validate_certs,
                True,
                True,
                no_deps,
                allow_pre_release=allow_pre_release,
            )

        requirements = []
        display.display("Starting collection download process to '%s'" % output_path)
        with _display_progress():
            for name, requirement in dep_map.items():
                collection_filename = "%s-%s-%s.tar.gz" % (
                    requirement.namespace,
                    requirement.name,
                    requirement.latest_version,
                )
                dest_path = os.path.join(output_path, collection_filename)
                requirements.append(
                    {"name": collection_filename, "version": requirement.latest_version}
                )

                display.display(
                    "Downloading collection '%s' to '%s'" % (name, dest_path)
                )

                if (
                    requirement.api is None
                    and requirement.b_path
                    and os.path.isfile(requirement.b_path)
                ):
                    shutil.copy(
                        requirement.b_path,
                        to_bytes(dest_path, errors="surrogate_or_strict"),
                    )
                elif requirement.api is None and requirement.b_path:
                    temp_path = to_text(b_temp_path, errors="surrogate_or_string")
                    temp_download_path = build_collection(
                        requirement.b_path, temp_path, True
                    )
                    shutil.move(
                        to_bytes(temp_download_path, errors="surrogate_or_strict"),
                        to_bytes(dest_path, errors="surrogate_or_strict"),
                    )
                else:
                    b_temp_download_path = requirement.download(b_temp_path)
                    shutil.move(
                        b_temp_download_path,
                        to_bytes(dest_path, errors="surrogate_or_strict"),
                    )

                display.display(
                    "%s (%s) was downloaded successfully"
                    % (name, requirement.latest_version)
                )

            requirements_path = os.path.join(output_path, "requirements.yml")
            display.display(
                "Writing requirements.yml file of downloaded collections to '%s'"
                % requirements_path
            )
            with open(
                to_bytes(requirements_path, errors="surrogate_or_strict"), mode="wb"
            ) as req_fd:
                req_fd.write(
                    to_bytes(
                        yaml.safe_dump({"collections": requirements}),
                        errors="surrogate_or_strict",
                    )
                )