Repository: pypa/pip

Issue:
new dependency resolver: using a requirements file doesn't produces expected conflict message

**What did you want to do?**
```
# pyenv 3.8.1 virtualenv
# https://github.com/scylladb/scylla-cluster-tests/blob/master/requirements-python.txt
pip install --use-feature=2020-resolver -r requirements-python.txt
```

**Output**
```
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'
```

**Additional information**

<!--
It would be great if you could also include your dependency tree. For this you can use pipdeptree: https://pypi.org/project/pipdeptree/

For users installing packages from a private repository or local directory, please try your best to describe your setup. We'd like to understand how to reproduce the error locally, so would need (at a minimum) a description of the packages you are trying to install, and a list of dependencies for each package.
-->



Discussion:
(Pasting in a paraphrase from Discord...)

Seems like the underlying issue here is likely a bug in the requirements file (that the versions of awscli and boto depend on mutually unsatisfiable versions of s3transfer), but still seems like there's a pip bug in the second part of that exception (not showing clearly what things caused an unsatisfiable install), and definitely seems the exception is going to be hard to decipher overall.
Interestingly... something seems related to the dependency resolution failing from a requirements file vs. otherwise.

Compare what happens when installing these conflicting packages via CLI params:

```
~/Development/pip is a git repository on master
⊙  venv/bin/pip install --use-feature=2020-resolver boto3==1.13.5 awscli==1.16.180                                                                                                              julian@Air ●
Collecting boto3==1.13.5
  Using cached boto3-1.13.5-py2.py3-none-any.whl (128 kB)
Collecting awscli==1.16.180
  Using cached awscli-1.16.180-py2.py3-none-any.whl (1.6 MB)
ERROR: Cannot install boto3 1.13.5 and awscli 1.16.180 because these package versions have conflicting dependencies.

The conflict is caused by:
    boto3 1.13.5 depends on botocore<1.17.0 and >=1.16.5
    awscli 1.16.180 depends on botocore==1.12.170

To fix this you could try to:
1. loosen the range of package versions you've specified
2. remove package versions to allow pip attempt to solve the dependency conflict

ERROR: ResolutionImpossible For help visit: https://pip.pypa.io/en/stable/user_guide/#fixing-conflicting-dependencies
```

to the exact same arguments provided as a requirements file:

```
~/Development/pip is a git repository on master
⊙  venv/bin/pip install --use-feature=2020-resolver -r =(printf 'boto3==1.13.5\nawscli==1.16.180')                                                                                              julian@Air ●
Collecting boto3==1.13.5
  Using cached boto3-1.13.5-py2.py3-none-any.whl (128 kB)
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 "/Users/julian/Development/pip/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/resolver.py", line 120, in resolve
    self._result = resolver.resolve(
  File "/Users/julian/Development/pip/venv/lib/python3.8/site-packages/pip/_vendor/resolvelib/resolvers.py", line 427, in resolve
    state = resolution.resolve(requirements, max_rounds=max_rounds)
  File "/Users/julian/Development/pip/venv/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('botocore<1.17.0,>=1.16.5'), 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('botocore==1.12.170'), 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 "/Users/julian/Development/pip/venv/lib/python3.8/site-packages/pip/_internal/cli/base_command.py", line 216, in _main
    status = self.run(options, args)
  File "/Users/julian/Development/pip/venv/lib/python3.8/site-packages/pip/_internal/cli/req_command.py", line 182, in wrapper
    return func(self, options, args)
  File "/Users/julian/Development/pip/venv/lib/python3.8/site-packages/pip/_internal/commands/install.py", line 323, in run
    requirement_set = resolver.resolve(
  File "/Users/julian/Development/pip/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/resolver.py", line 125, in resolve
    error = self.factory.get_installation_error(e)
  File "/Users/julian/Development/pip/venv/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'
```

which produces the traceback from this issue.
Can you provide your `requirements-python.txt`, or something that can be used to replicate this error?

Resolving patch:
diff --git a/src/pip/_internal/resolution/resolvelib/factory.py b/src/pip/_internal/resolution/resolvelib/factory.py
--- a/src/pip/_internal/resolution/resolvelib/factory.py
+++ b/src/pip/_internal/resolution/resolvelib/factory.py
@@ -11,6 +11,7 @@
     UnsupportedWheel,
 )
 from pip._internal.models.wheel import Wheel
+from pip._internal.req.req_install import InstallRequirement
 from pip._internal.utils.compatibility_tags import get_supported
 from pip._internal.utils.hashes import Hashes
 from pip._internal.utils.misc import (
@@ -56,7 +57,6 @@
     from pip._internal.index.package_finder import PackageFinder
     from pip._internal.models.link import Link
     from pip._internal.operations.prepare import RequirementPreparer
-    from pip._internal.req.req_install import InstallRequirement
     from pip._internal.resolution.base import InstallRequirementProvider
 
     from .base import Candidate, Requirement
@@ -406,22 +406,22 @@ 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:
-                ireq = parent.get_install_requirement()
-                if ireq and ireq.comes_from:
-                    trigger = "{}".format(
-                        ireq.comes_from.name
-                    )
-                else:
-                    trigger = "{} {}".format(
-                        parent.name,
-                        parent.version
-                    )
+                trigger = describe_trigger(parent)
             triggers.append(trigger)
 
         if triggers:


Test patch:
diff --git a/tests/functional/test_new_resolver_errors.py b/tests/functional/test_new_resolver_errors.py
new file mode 100644
--- /dev/null
+++ b/tests/functional/test_new_resolver_errors.py
@@ -0,0 +1,26 @@
+from tests.lib import create_basic_wheel_for_package
+
+
+def test_new_resolver_conflict_requirements_file(tmpdir, script):
+    create_basic_wheel_for_package(script, "base", "1.0")
+    create_basic_wheel_for_package(script, "base", "2.0")
+    create_basic_wheel_for_package(
+        script, "pkga", "1.0", depends=["base==1.0"],
+    )
+    create_basic_wheel_for_package(
+        script, "pkgb", "1.0", depends=["base==2.0"],
+    )
+
+    req_file = tmpdir.joinpath("requirements.txt")
+    req_file.write_text("pkga\npkgb")
+
+    result = script.pip(
+        "install", "--use-feature=2020-resolver",
+        "--no-cache-dir", "--no-index",
+        "--find-links", script.scratch_path,
+        "-r", req_file,
+        expect_error=True,
+    )
+
+    message = "package versions have conflicting dependencies"
+    assert message in result.stderr, str(result)