Repository: pypa/pip

Issue:
Redundant "Requirement already satisfied" output in 20.3b1
**Environment**

* pip version: 20.3b1
* Python version: Python 3.7.9
* OS: Ubuntu 16.04

**Description**

Starting with version 20.3b1, pip spams many `Requirement already satisfied` messages.

**Expected behavior**

In earlier versions the messages was just printed once per package, which was much nicer behavior.

**How to Reproduce**

A simple example `pip install kubernetes plotly protobuf six` (on second execution):

```
Requirement already satisfied: kubernetes in ./tmp_venv/lib/python3.7/site-packages (12.0.0)
Requirement already satisfied: plotly in ./tmp_venv/lib/python3.7/site-packages (4.11.0)
Requirement already satisfied: protobuf in ./tmp_venv/lib/python3.7/site-packages (3.13.0)
Requirement already satisfied: six in ./tmp_venv/lib/python3.7/site-packages (1.15.0)
Requirement already satisfied: six in ./tmp_venv/lib/python3.7/site-packages (1.15.0)
Requirement already satisfied: urllib3>=1.24.2 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (1.25.10)
Requirement already satisfied: websocket-client!=0.40.0,!=0.41.*,!=0.42.*,>=0.32.0 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (0.57.0)
Requirement already satisfied: requests-oauthlib in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (1.3.0)
Requirement already satisfied: pyyaml>=3.12 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (3.13)
Requirement already satisfied: setuptools>=21.0.0 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (50.3.2)
Requirement already satisfied: google-auth>=1.0.1 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (1.22.1)
Requirement already satisfied: certifi>=14.05.14 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (2020.6.20)
Requirement already satisfied: python-dateutil>=2.5.3 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (2.8.1)
Requirement already satisfied: requests in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (2.24.0)
Requirement already satisfied: six in ./tmp_venv/lib/python3.7/site-packages (1.15.0)
Requirement already satisfied: retrying>=1.3.3 in ./tmp_venv/lib/python3.7/site-packages (from plotly) (1.3.3)
Requirement already satisfied: setuptools>=21.0.0 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (50.3.2)
Requirement already satisfied: six in ./tmp_venv/lib/python3.7/site-packages (1.15.0)
Requirement already satisfied: six in ./tmp_venv/lib/python3.7/site-packages (1.15.0)
Requirement already satisfied: oauthlib>=3.0.0 in ./tmp_venv/lib/python3.7/site-packages (from requests-oauthlib->kubernetes) (3.1.0)
Requirement already satisfied: requests in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (2.24.0)
Requirement already satisfied: pyasn1-modules>=0.2.1 in ./tmp_venv/lib/python3.7/site-packages (from google-auth>=1.0.1->kubernetes) (0.2.8)
Requirement already satisfied: six in ./tmp_venv/lib/python3.7/site-packages (1.15.0)
Requirement already satisfied: rsa<5,>=3.1.4 in ./tmp_venv/lib/python3.7/site-packages (from google-auth>=1.0.1->kubernetes) (4.6)
Requirement already satisfied: cachetools<5.0,>=2.0.0 in ./tmp_venv/lib/python3.7/site-packages (from google-auth>=1.0.1->kubernetes) (4.1.1)
Requirement already satisfied: setuptools>=21.0.0 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (50.3.2)
Requirement already satisfied: six in ./tmp_venv/lib/python3.7/site-packages (1.15.0)
Requirement already satisfied: urllib3>=1.24.2 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (1.25.10)
Requirement already satisfied: chardet<4,>=3.0.2 in ./tmp_venv/lib/python3.7/site-packages (from requests->kubernetes) (3.0.4)
Requirement already satisfied: certifi>=14.05.14 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (2020.6.20)
Requirement already satisfied: idna<3,>=2.5 in ./tmp_venv/lib/python3.7/site-packages (from requests->kubernetes) (2.10)
Requirement already satisfied: six in ./tmp_venv/lib/python3.7/site-packages (1.15.0)
Requirement already satisfied: pyasn1<0.5.0,>=0.4.6 in ./tmp_venv/lib/python3.7/site-packages (from pyasn1-modules>=0.2.1->google-auth>=1.0.1->kubernetes) (0.4.8)
Requirement already satisfied: pyasn1<0.5.0,>=0.4.6 in ./tmp_venv/lib/python3.7/site-packages (from pyasn1-modules>=0.2.1->google-auth>=1.0.1->kubernetes) (0.4.8)
```

For instance the message that `six` is already satisfied is shown 8 times.

For comparison this is the output with the latest pre 20.3b1 version:

```
Requirement already satisfied: kubernetes in ./tmp_venv/lib/python3.7/site-packages (12.0.0)
Requirement already satisfied: plotly in ./tmp_venv/lib/python3.7/site-packages (4.11.0)
Requirement already satisfied: protobuf in ./tmp_venv/lib/python3.7/site-packages (3.13.0)
Requirement already satisfied: six in ./tmp_venv/lib/python3.7/site-packages (1.15.0)
Requirement already satisfied: google-auth>=1.0.1 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (1.22.1)
Requirement already satisfied: requests in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (2.24.0)
Requirement already satisfied: websocket-client!=0.40.0,!=0.41.*,!=0.42.*,>=0.32.0 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (0.57.0)
Requirement already satisfied: pyyaml>=3.12 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (3.13)
Requirement already satisfied: setuptools>=21.0.0 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (50.3.2)
Requirement already satisfied: python-dateutil>=2.5.3 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (2.8.1)
Requirement already satisfied: urllib3>=1.24.2 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (1.25.10)
Requirement already satisfied: requests-oauthlib in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (1.3.0)
Requirement already satisfied: certifi>=14.05.14 in ./tmp_venv/lib/python3.7/site-packages (from kubernetes) (2020.6.20)
Requirement already satisfied: retrying>=1.3.3 in ./tmp_venv/lib/python3.7/site-packages (from plotly) (1.3.3)
Requirement already satisfied: pyasn1-modules>=0.2.1 in ./tmp_venv/lib/python3.7/site-packages (from google-auth>=1.0.1->kubernetes) (0.2.8)
Requirement already satisfied: cachetools<5.0,>=2.0.0 in ./tmp_venv/lib/python3.7/site-packages (from google-auth>=1.0.1->kubernetes) (4.1.1)
Requirement already satisfied: rsa<5,>=3.1.4; python_version >= "3.5" in ./tmp_venv/lib/python3.7/site-packages (from google-auth>=1.0.1->kubernetes) (4.6)
Requirement already satisfied: idna<3,>=2.5 in ./tmp_venv/lib/python3.7/site-packages (from requests->kubernetes) (2.10)
Requirement already satisfied: chardet<4,>=3.0.2 in ./tmp_venv/lib/python3.7/site-packages (from requests->kubernetes) (3.0.4)
Requirement already satisfied: oauthlib>=3.0.0 in ./tmp_venv/lib/python3.7/site-packages (from requests-oauthlib->kubernetes) (3.1.0)
Requirement already satisfied: pyasn1<0.5.0,>=0.4.6 in ./tmp_venv/lib/python3.7/site-packages (from pyasn1-modules>=0.2.1->google-auth>=1.0.1->kubernetes) (0.4.8)
```

In one of our large projects for instance, the number of output lines has increased from 226 messages to 535 messages. Also, the new behavior has an actually observable performance impact: 
- 2.3 seconds in 20.2.4
- 3.8 seconds in 20.3b1


**Output**

See inline above.



Discussion:
Thank you for the report, @bluenote10! I'm bringing it to the attention of our user experience folks who can consider it as they work on plans and principles for improving pip's output.

Also, you may be interested in #8664 which is an issue specifically about pip's performance with the new resolver.

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
@@ -96,6 +96,8 @@ def __init__(
 
         self._link_candidate_cache = {}  # type: Cache[LinkCandidate]
         self._editable_candidate_cache = {}  # type: Cache[EditableCandidate]
+        self._installed_candidate_cache = {
+        }  # type: Dict[str, AlreadyInstalledCandidate]
 
         if not ignore_installed:
             self._installed_dists = {
@@ -117,7 +119,11 @@ def _make_candidate_from_dist(
         template,  # type: InstallRequirement
     ):
         # type: (...) -> Candidate
-        base = AlreadyInstalledCandidate(dist, template, factory=self)
+        try:
+            base = self._installed_candidate_cache[dist.key]
+        except KeyError:
+            base = AlreadyInstalledCandidate(dist, template, factory=self)
+            self._installed_candidate_cache[dist.key] = base
         if extras:
             return ExtrasCandidate(base, extras)
         return base


Test patch: