# Buggy code
def get_installation_error(self, e):
    # type: (ResolutionImpossible) -> InstallationError

    assert e.causes, "Installation error reported with no cause"

    # If one of the things we can't solve is "we need Python X.Y",
    # that is what we report.
    for cause in e.causes:
        if isinstance(cause.requirement, RequiresPythonRequirement):
            return self._report_requires_python_error(
                cause.requirement,
                cause.parent,
            )

    # Otherwise, we have a set of causes which can't all be satisfied
    # at once.

    # The simplest case is when we have *one* cause that can't be
    # satisfied. We just report that case.
    if len(e.causes) == 1:
        req, parent = e.causes[0]
        if parent is None:
            req_disp = str(req)
        else:
            req_disp = "{} (from {})".format(req, parent.name)
        logger.critical(
            "Could not find a version that satisfies the requirement %s",
            req_disp,
        )
        return DistributionNotFound("No matching distribution found for {}".format(req))

    # OK, we now have a list of requirements that can't all be
    # satisfied at once.

    # A couple of formatting helpers
    def text_join(parts):
        # type: (List[str]) -> str
        if len(parts) == 1:
            return parts[0]

        return ", ".join(parts[:-1]) + " and " + parts[-1]

    def readable_form(cand):
        # type: (Candidate) -> str
        return "{} {}".format(cand.name, cand.version)

    triggers = []
    for req, parent in e.causes:
        if parent is None:
            # This is a root requirement, so we can report it directly
            trigger = req.format_for_error()
        else:
            ireq = parent.get_install_requirement()
            if ireq and ireq.comes_from:
                trigger = "{}".format(ireq.comes_from.name)
            else:
                trigger = "{} {}".format(parent.name, parent.version)
        triggers.append(trigger)

    if triggers:
        info = text_join(triggers)
    else:
        info = "the requested packages"

    msg = (
        "Cannot install {} because these package versions "
        "have conflicting dependencies.".format(info)
    )
    logger.critical(msg)
    msg = "\nThe conflict is caused by:"
    for req, parent in e.causes:
        msg = msg + "\n    "
        if parent:
            msg = msg + "{} {} depends on ".format(parent.name, parent.version)
        else:
            msg = msg + "The user requested "
        msg = msg + req.format_for_error()

    msg = (
        msg
        + "\n\n"
        + "To fix this you could try to:\n"
        + "1. loosen the range of package versions you've specified\n"
        + "2. remove package versions to allow pip attempt to solve "
        + "the dependency conflict\n"
    )

    logger.info(msg)

    return DistributionNotFound(
        "ResolutionImpossible For help visit: "
        "https://pip.pypa.io/en/stable/user_guide/"
        "#fixing-conflicting-dependencies"
    )


# Traceback (AttributeError)
Collecting https://github.com/fruch/repodataParser/archive/py3.zip (from -r requirements-python.txt (line 25))
Using cached https://github.com/fruch/repodataParser/archive/py3.zip
Processing /home/fruch/.cache/pip/wheels/ad/56/bc/1522f864feb2a358ea6f1a92b4798d69ac783a28e80567a18b/PyYAML-5.1-cp38-cp38-linux_x86_64.whl
Collecting paramiko==2.7.1
Using cached paramiko-2.7.1-py2.py3-none-any.whl (206 kB)
Collecting fabric==2.5.0
Using cached fabric-2.5.0-py2.py3-none-any.whl (51 kB)
Collecting invoke==1.3.1
Using cached invoke-1.3.1-py3-none-any.whl (207 kB)
Collecting boto3==1.13.5
Using cached boto3-1.13.5-py2.py3-none-any.whl (128 kB)
Processing /home/fruch/.cache/pip/wheels/f2/14/9f/b8030bd9a8b507e4a3fe0d0e73c91e9d0794ee0aadbd67056d/boto3_stubs-1.13.5.0-py3-none-any.whl
Collecting awscli==1.16.180
Using cached awscli-1.16.180-py2.py3-none-any.whl (1.6 MB)
ERROR: Exception:
Traceback (most recent call last):
File "/home/fruch/.pyenv/versions/3.8.1/envs/test-pip/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/resolver.py", line 120, in resolve
self._result = resolver.resolve(
File "/home/fruch/.pyenv/versions/3.8.1/envs/test-pip/lib/python3.8/site-packages/pip/_vendor/resolvelib/resolvers.py", line 427, in resolve
state = resolution.resolve(requirements, max_rounds=max_rounds)
File "/home/fruch/.pyenv/versions/3.8.1/envs/test-pip/lib/python3.8/site-packages/pip/_vendor/resolvelib/resolvers.py", line 333, in resolve
raise ResolutionImpossible(causes)
pip._vendor.resolvelib.resolvers.ResolutionImpossible: [RequirementInformation(requirement=SpecifierRequirement('s3transfer<0.4.0,>=0.3.0'), parent=LinkCandidate('https://files.pythonhosted.org/packages/fa/78/9e5ca4b57dd8f358a774f85e69ae5423cb8888c5a8f6908c9e41af562372/boto3-1.13.5-py2.py3-none-any.whl#sha256=60d9833685713815fb3fd72a513813a2810d75179c35d781388825a09cccf6cb (from https://pypi.org/simple/boto3/)')), RequirementInformation(requirement=SpecifierRequirement('s3transfer<0.3.0,>=0.2.0'), parent=LinkCandidate('https://files.pythonhosted.org/packages/24/3d/632f17377d5fc43f51658009f9cfd79c549d104915da7cb2977ac4c0b9e8/awscli-1.16.180-py2.py3-none-any.whl#sha256=e32973f8c03eaf81800aca423aa9c1daa05613b4f2827ae4e98e032fe4511495 (from https://pypi.org/simple/awscli/)'))]

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/home/fruch/.pyenv/versions/3.8.1/envs/test-pip/lib/python3.8/site-packages/pip/_internal/cli/base_command.py", line 216, in _main
status = self.run(options, args)
File "/home/fruch/.pyenv/versions/3.8.1/envs/test-pip/lib/python3.8/site-packages/pip/_internal/cli/req_command.py", line 182, in wrapper
return func(self, options, args)
File "/home/fruch/.pyenv/versions/3.8.1/envs/test-pip/lib/python3.8/site-packages/pip/_internal/commands/install.py", line 323, in run
requirement_set = resolver.resolve(
File "/home/fruch/.pyenv/versions/3.8.1/envs/test-pip/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/resolver.py", line 125, in resolve
error = self.factory.get_installation_error(e)
File "/home/fruch/.pyenv/versions/3.8.1/envs/test-pip/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/factory.py", line 418, in get_installation_error
ireq.comes_from.name
AttributeError: 'str' object has no attribute 'name'

# Fixed code
def get_installation_error(self, e):
    # type: (ResolutionImpossible) -> InstallationError

    assert e.causes, "Installation error reported with no cause"

    # If one of the things we can't solve is "we need Python X.Y",
    # that is what we report.
    for cause in e.causes:
        if isinstance(cause.requirement, RequiresPythonRequirement):
            return self._report_requires_python_error(
                cause.requirement,
                cause.parent,
            )

    # Otherwise, we have a set of causes which can't all be satisfied
    # at once.

    # The simplest case is when we have *one* cause that can't be
    # satisfied. We just report that case.
    if len(e.causes) == 1:
        req, parent = e.causes[0]
        if parent is None:
            req_disp = str(req)
        else:
            req_disp = "{} (from {})".format(req, parent.name)
        logger.critical(
            "Could not find a version that satisfies the requirement %s",
            req_disp,
        )
        return DistributionNotFound("No matching distribution found for {}".format(req))

    # OK, we now have a list of requirements that can't all be
    # satisfied at once.

    # A couple of formatting helpers
    def text_join(parts):
        # type: (List[str]) -> str
        if len(parts) == 1:
            return parts[0]

        return ", ".join(parts[:-1]) + " and " + parts[-1]

    def readable_form(cand):
        # type: (Candidate) -> str
        return "{} {}".format(cand.name, cand.version)

    def describe_trigger(parent):
        # type: (Candidate) -> str
        ireq = parent.get_install_requirement()
        if not ireq or not ireq.comes_from:
            return "{} {}".format(parent.name, parent.version)
        if isinstance(ireq.comes_from, InstallRequirement):
            return str(ireq.comes_from.name)
        return str(ireq.comes_from)

    triggers = []
    for req, parent in e.causes:
        if parent is None:
            # This is a root requirement, so we can report it directly
            trigger = req.format_for_error()
        else:
            trigger = describe_trigger(parent)
        triggers.append(trigger)

    if triggers:
        info = text_join(triggers)
    else:
        info = "the requested packages"

    msg = (
        "Cannot install {} because these package versions "
        "have conflicting dependencies.".format(info)
    )
    logger.critical(msg)
    msg = "\nThe conflict is caused by:"
    for req, parent in e.causes:
        msg = msg + "\n    "
        if parent:
            msg = msg + "{} {} depends on ".format(parent.name, parent.version)
        else:
            msg = msg + "The user requested "
        msg = msg + req.format_for_error()

    msg = (
        msg
        + "\n\n"
        + "To fix this you could try to:\n"
        + "1. loosen the range of package versions you've specified\n"
        + "2. remove package versions to allow pip attempt to solve "
        + "the dependency conflict\n"
    )

    logger.info(msg)

    return DistributionNotFound(
        "ResolutionImpossible For help visit: "
        "https://pip.pypa.io/en/stable/user_guide/"
        "#fixing-conflicting-dependencies"
    )