We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Package left un-upgraded with `pip install --upgrade`
### Description

When upgrading both `datasets` and `fsspec` in an environment, only `datasets` was upgraded to the latest version even though there was nothing constraining `fsspec` from being updated. `fsspec` is a dependency of `datasets` (`fsspec[http]>=2021.05.0` is specified in its [`setup.py`](https://github.com/huggingface/datasets/blob/1.14.0/setup.py)), and from looking at the resolver's debug output, it seems that pip is preferring to use the pre-installed version of `fsspec` instead of the newer one.

The documentation for `--upgrade` says, "Upgrade all specified packages to the newest available version." Unless I'm misunderstanding, all packages explicitly listed on the command line should have been updated if possible.

### Expected behavior

`fsspec` should be upgraded to `2021.10.1`.

### pip version

21.3.1

### Python version

3.7.12

### OS

macOS 10.15.7

### How to Reproduce

1. Create a file named `pip_fsspec_test.sh` with the following contents:

```bash
#!/usr/bin/env bash

set -euxo pipefail

# Prepare environment
python --version
pip install -U pip setuptools wheel
pip list

# Install outdated packages
pip install datasets==1.13.3 fsspec==2021.10.0
pip list --outdated

# Attempt to upgrade both packages
pip install -U 'datasets<=1.14.0' 'fsspec<=2021.10.1'
pip list --outdated
```

2. Run `bash pip_fsspec_test.sh &> pip_fsspec_log.txt` in a new environment to create [pip_fsspec_log.txt](https://github.com/pypa/pip/files/7402239/pip_fsspec_log.txt).
3. Change the penultimate line of the script to `PIP_RESOLVER_DEBUG=1 pip -vvv install -U 'datasets<=1.14.0' 'fsspec<=2021.10.1'`.
4. Run `bash pip_fsspec_test.sh &> pip_fsspec_verbose_log.txt` in a new environment to create [pip_fsspec_verbose_log.txt](https://github.com/pypa/pip/files/7402240/pip_fsspec_verbose_log.txt).

### Output

_See files under "How to Reproduce"._

### Code of Conduct

- [X] I agree to follow the [PSF Code of Conduct](https://www.python.org/psf/conduct/).

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/pip/_internal/resolution/resolvelib/provider.py`
Content:
```
1 import collections
2 import math
3 from typing import TYPE_CHECKING, Dict, Iterable, Iterator, Mapping, Sequence, Union
4 
5 from pip._vendor.resolvelib.providers import AbstractProvider
6 
7 from .base import Candidate, Constraint, Requirement
8 from .candidates import REQUIRES_PYTHON_IDENTIFIER
9 from .factory import Factory
10 
11 if TYPE_CHECKING:
12     from pip._vendor.resolvelib.providers import Preference
13     from pip._vendor.resolvelib.resolvers import RequirementInformation
14 
15     PreferenceInformation = RequirementInformation[Requirement, Candidate]
16 
17     _ProviderBase = AbstractProvider[Requirement, Candidate, str]
18 else:
19     _ProviderBase = AbstractProvider
20 
21 # Notes on the relationship between the provider, the factory, and the
22 # candidate and requirement classes.
23 #
24 # The provider is a direct implementation of the resolvelib class. Its role
25 # is to deliver the API that resolvelib expects.
26 #
27 # Rather than work with completely abstract "requirement" and "candidate"
28 # concepts as resolvelib does, pip has concrete classes implementing these two
29 # ideas. The API of Requirement and Candidate objects are defined in the base
30 # classes, but essentially map fairly directly to the equivalent provider
31 # methods. In particular, `find_matches` and `is_satisfied_by` are
32 # requirement methods, and `get_dependencies` is a candidate method.
33 #
34 # The factory is the interface to pip's internal mechanisms. It is stateless,
35 # and is created by the resolver and held as a property of the provider. It is
36 # responsible for creating Requirement and Candidate objects, and provides
37 # services to those objects (access to pip's finder and preparer).
38 
39 
40 class PipProvider(_ProviderBase):
41     """Pip's provider implementation for resolvelib.
42 
43     :params constraints: A mapping of constraints specified by the user. Keys
44         are canonicalized project names.
45     :params ignore_dependencies: Whether the user specified ``--no-deps``.
46     :params upgrade_strategy: The user-specified upgrade strategy.
47     :params user_requested: A set of canonicalized package names that the user
48         supplied for pip to install/upgrade.
49     """
50 
51     def __init__(
52         self,
53         factory: Factory,
54         constraints: Dict[str, Constraint],
55         ignore_dependencies: bool,
56         upgrade_strategy: str,
57         user_requested: Dict[str, int],
58     ) -> None:
59         self._factory = factory
60         self._constraints = constraints
61         self._ignore_dependencies = ignore_dependencies
62         self._upgrade_strategy = upgrade_strategy
63         self._user_requested = user_requested
64         self._known_depths: Dict[str, float] = collections.defaultdict(lambda: math.inf)
65 
66     def identify(self, requirement_or_candidate: Union[Requirement, Candidate]) -> str:
67         return requirement_or_candidate.name
68 
69     def get_preference(  # type: ignore
70         self,
71         identifier: str,
72         resolutions: Mapping[str, Candidate],
73         candidates: Mapping[str, Iterator[Candidate]],
74         information: Mapping[str, Iterable["PreferenceInformation"]],
75         backtrack_causes: Sequence["PreferenceInformation"],
76     ) -> "Preference":
77         """Produce a sort key for given requirement based on preference.
78 
79         The lower the return value is, the more preferred this group of
80         arguments is.
81 
82         Currently pip considers the followings in order:
83 
84         * Prefer if any of the known requirements is "direct", e.g. points to an
85           explicit URL.
86         * If equal, prefer if any requirement is "pinned", i.e. contains
87           operator ``===`` or ``==``.
88         * If equal, calculate an approximate "depth" and resolve requirements
89           closer to the user-specified requirements first.
90         * Order user-specified requirements by the order they are specified.
91         * If equal, prefers "non-free" requirements, i.e. contains at least one
92           operator, such as ``>=`` or ``<``.
93         * If equal, order alphabetically for consistency (helps debuggability).
94         """
95         lookups = (r.get_candidate_lookup() for r, _ in information[identifier])
96         candidate, ireqs = zip(*lookups)
97         operators = [
98             specifier.operator
99             for specifier_set in (ireq.specifier for ireq in ireqs if ireq)
100             for specifier in specifier_set
101         ]
102 
103         direct = candidate is not None
104         pinned = any(op[:2] == "==" for op in operators)
105         unfree = bool(operators)
106 
107         try:
108             requested_order: Union[int, float] = self._user_requested[identifier]
109         except KeyError:
110             requested_order = math.inf
111             parent_depths = (
112                 self._known_depths[parent.name] if parent is not None else 0.0
113                 for _, parent in information[identifier]
114             )
115             inferred_depth = min(d for d in parent_depths) + 1.0
116         else:
117             inferred_depth = 1.0
118         self._known_depths[identifier] = inferred_depth
119 
120         requested_order = self._user_requested.get(identifier, math.inf)
121 
122         # Requires-Python has only one candidate and the check is basically
123         # free, so we always do it first to avoid needless work if it fails.
124         requires_python = identifier == REQUIRES_PYTHON_IDENTIFIER
125 
126         # HACK: Setuptools have a very long and solid backward compatibility
127         # track record, and extremely few projects would request a narrow,
128         # non-recent version range of it since that would break a lot things.
129         # (Most projects specify it only to request for an installer feature,
130         # which does not work, but that's another topic.) Intentionally
131         # delaying Setuptools helps reduce branches the resolver has to check.
132         # This serves as a temporary fix for issues like "apache-airflow[all]"
133         # while we work on "proper" branch pruning techniques.
134         delay_this = identifier == "setuptools"
135 
136         # Prefer the causes of backtracking on the assumption that the problem
137         # resolving the dependency tree is related to the failures that caused
138         # the backtracking
139         backtrack_cause = self.is_backtrack_cause(identifier, backtrack_causes)
140 
141         return (
142             not requires_python,
143             delay_this,
144             not direct,
145             not pinned,
146             not backtrack_cause,
147             inferred_depth,
148             requested_order,
149             not unfree,
150             identifier,
151         )
152 
153     def _get_constraint(self, identifier: str) -> Constraint:
154         if identifier in self._constraints:
155             return self._constraints[identifier]
156 
157         # HACK: Theoretically we should check whether this identifier is a valid
158         # "NAME[EXTRAS]" format, and parse out the name part with packaging or
159         # some regular expression. But since pip's resolver only spits out
160         # three kinds of identifiers: normalized PEP 503 names, normalized names
161         # plus extras, and Requires-Python, we can cheat a bit here.
162         name, open_bracket, _ = identifier.partition("[")
163         if open_bracket and name in self._constraints:
164             return self._constraints[name]
165 
166         return Constraint.empty()
167 
168     def find_matches(
169         self,
170         identifier: str,
171         requirements: Mapping[str, Iterator[Requirement]],
172         incompatibilities: Mapping[str, Iterator[Candidate]],
173     ) -> Iterable[Candidate]:
174         def _eligible_for_upgrade(name: str) -> bool:
175             """Are upgrades allowed for this project?
176 
177             This checks the upgrade strategy, and whether the project was one
178             that the user specified in the command line, in order to decide
179             whether we should upgrade if there's a newer version available.
180 
181             (Note that we don't need access to the `--upgrade` flag, because
182             an upgrade strategy of "to-satisfy-only" means that `--upgrade`
183             was not specified).
184             """
185             if self._upgrade_strategy == "eager":
186                 return True
187             elif self._upgrade_strategy == "only-if-needed":
188                 return name in self._user_requested
189             return False
190 
191         return self._factory.find_candidates(
192             identifier=identifier,
193             requirements=requirements,
194             constraint=self._get_constraint(identifier),
195             prefers_installed=(not _eligible_for_upgrade(identifier)),
196             incompatibilities=incompatibilities,
197         )
198 
199     def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> bool:
200         return requirement.is_satisfied_by(candidate)
201 
202     def get_dependencies(self, candidate: Candidate) -> Sequence[Requirement]:
203         with_requires = not self._ignore_dependencies
204         return [r for r in candidate.iter_dependencies(with_requires) if r is not None]
205 
206     @staticmethod
207     def is_backtrack_cause(
208         identifier: str, backtrack_causes: Sequence["PreferenceInformation"]
209     ) -> bool:
210         for backtrack_cause in backtrack_causes:
211             if identifier == backtrack_cause.requirement.name:
212                 return True
213             if backtrack_cause.parent and identifier == backtrack_cause.parent.name:
214                 return True
215         return False
216
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/pip/_internal/resolution/resolvelib/provider.py b/src/pip/_internal/resolution/resolvelib/provider.py
--- a/src/pip/_internal/resolution/resolvelib/provider.py
+++ b/src/pip/_internal/resolution/resolvelib/provider.py
@@ -1,6 +1,15 @@
 import collections
 import math
-from typing import TYPE_CHECKING, Dict, Iterable, Iterator, Mapping, Sequence, Union
+from typing import (
+    TYPE_CHECKING,
+    Dict,
+    Iterable,
+    Iterator,
+    Mapping,
+    Sequence,
+    TypeVar,
+    Union,
+)
 
 from pip._vendor.resolvelib.providers import AbstractProvider
 
@@ -37,6 +46,35 @@
 # services to those objects (access to pip's finder and preparer).
 
 
+D = TypeVar("D")
+V = TypeVar("V")
+
+
+def _get_with_identifier(
+    mapping: Mapping[str, V],
+    identifier: str,
+    default: D,
+) -> Union[D, V]:
+    """Get item from a package name lookup mapping with a resolver identifier.
+
+    This extra logic is needed when the target mapping is keyed by package
+    name, which cannot be directly looked up with an identifier (which may
+    contain requested extras). Additional logic is added to also look up a value
+    by "cleaning up" the extras from the identifier.
+    """
+    if identifier in mapping:
+        return mapping[identifier]
+    # HACK: Theoretically we should check whether this identifier is a valid
+    # "NAME[EXTRAS]" format, and parse out the name part with packaging or
+    # some regular expression. But since pip's resolver only spits out three
+    # kinds of identifiers: normalized PEP 503 names, normalized names plus
+    # extras, and Requires-Python, we can cheat a bit here.
+    name, open_bracket, _ = identifier.partition("[")
+    if open_bracket and name in mapping:
+        return mapping[name]
+    return default
+
+
 class PipProvider(_ProviderBase):
     """Pip's provider implementation for resolvelib.
 
@@ -150,28 +188,13 @@
             identifier,
         )
 
-    def _get_constraint(self, identifier: str) -> Constraint:
-        if identifier in self._constraints:
-            return self._constraints[identifier]
-
-        # HACK: Theoretically we should check whether this identifier is a valid
-        # "NAME[EXTRAS]" format, and parse out the name part with packaging or
-        # some regular expression. But since pip's resolver only spits out
-        # three kinds of identifiers: normalized PEP 503 names, normalized names
-        # plus extras, and Requires-Python, we can cheat a bit here.
-        name, open_bracket, _ = identifier.partition("[")
-        if open_bracket and name in self._constraints:
-            return self._constraints[name]
-
-        return Constraint.empty()
-
     def find_matches(
         self,
         identifier: str,
         requirements: Mapping[str, Iterator[Requirement]],
         incompatibilities: Mapping[str, Iterator[Candidate]],
     ) -> Iterable[Candidate]:
-        def _eligible_for_upgrade(name: str) -> bool:
+        def _eligible_for_upgrade(identifier: str) -> bool:
             """Are upgrades allowed for this project?
 
             This checks the upgrade strategy, and whether the project was one
@@ -185,13 +208,23 @@
             if self._upgrade_strategy == "eager":
                 return True
             elif self._upgrade_strategy == "only-if-needed":
-                return name in self._user_requested
+                user_order = _get_with_identifier(
+                    self._user_requested,
+                    identifier,
+                    default=None,
+                )
+                return user_order is not None
             return False
 
+        constraint = _get_with_identifier(
+            self._constraints,
+            identifier,
+            default=Constraint.empty(),
+        )
         return self._factory.find_candidates(
             identifier=identifier,
             requirements=requirements,
-            constraint=self._get_constraint(identifier),
+            constraint=constraint,
             prefers_installed=(not _eligible_for_upgrade(identifier)),
             incompatibilities=incompatibilities,
         )

{"golden_diff": "diff --git a/src/pip/_internal/resolution/resolvelib/provider.py b/src/pip/_internal/resolution/resolvelib/provider.py\n--- a/src/pip/_internal/resolution/resolvelib/provider.py\n+++ b/src/pip/_internal/resolution/resolvelib/provider.py\n@@ -1,6 +1,15 @@\n import collections\n import math\n-from typing import TYPE_CHECKING, Dict, Iterable, Iterator, Mapping, Sequence, Union\n+from typing import (\n+    TYPE_CHECKING,\n+    Dict,\n+    Iterable,\n+    Iterator,\n+    Mapping,\n+    Sequence,\n+    TypeVar,\n+    Union,\n+)\n \n from pip._vendor.resolvelib.providers import AbstractProvider\n \n@@ -37,6 +46,35 @@\n # services to those objects (access to pip's finder and preparer).\n \n \n+D = TypeVar(\"D\")\n+V = TypeVar(\"V\")\n+\n+\n+def _get_with_identifier(\n+    mapping: Mapping[str, V],\n+    identifier: str,\n+    default: D,\n+) -> Union[D, V]:\n+    \"\"\"Get item from a package name lookup mapping with a resolver identifier.\n+\n+    This extra logic is needed when the target mapping is keyed by package\n+    name, which cannot be directly looked up with an identifier (which may\n+    contain requested extras). Additional logic is added to also look up a value\n+    by \"cleaning up\" the extras from the identifier.\n+    \"\"\"\n+    if identifier in mapping:\n+        return mapping[identifier]\n+    # HACK: Theoretically we should check whether this identifier is a valid\n+    # \"NAME[EXTRAS]\" format, and parse out the name part with packaging or\n+    # some regular expression. But since pip's resolver only spits out three\n+    # kinds of identifiers: normalized PEP 503 names, normalized names plus\n+    # extras, and Requires-Python, we can cheat a bit here.\n+    name, open_bracket, _ = identifier.partition(\"[\")\n+    if open_bracket and name in mapping:\n+        return mapping[name]\n+    return default\n+\n+\n class PipProvider(_ProviderBase):\n     \"\"\"Pip's provider implementation for resolvelib.\n \n@@ -150,28 +188,13 @@\n             identifier,\n         )\n \n-    def _get_constraint(self, identifier: str) -> Constraint:\n-        if identifier in self._constraints:\n-            return self._constraints[identifier]\n-\n-        # HACK: Theoretically we should check whether this identifier is a valid\n-        # \"NAME[EXTRAS]\" format, and parse out the name part with packaging or\n-        # some regular expression. But since pip's resolver only spits out\n-        # three kinds of identifiers: normalized PEP 503 names, normalized names\n-        # plus extras, and Requires-Python, we can cheat a bit here.\n-        name, open_bracket, _ = identifier.partition(\"[\")\n-        if open_bracket and name in self._constraints:\n-            return self._constraints[name]\n-\n-        return Constraint.empty()\n-\n     def find_matches(\n         self,\n         identifier: str,\n         requirements: Mapping[str, Iterator[Requirement]],\n         incompatibilities: Mapping[str, Iterator[Candidate]],\n     ) -> Iterable[Candidate]:\n-        def _eligible_for_upgrade(name: str) -> bool:\n+        def _eligible_for_upgrade(identifier: str) -> bool:\n             \"\"\"Are upgrades allowed for this project?\n \n             This checks the upgrade strategy, and whether the project was one\n@@ -185,13 +208,23 @@\n             if self._upgrade_strategy == \"eager\":\n                 return True\n             elif self._upgrade_strategy == \"only-if-needed\":\n-                return name in self._user_requested\n+                user_order = _get_with_identifier(\n+                    self._user_requested,\n+                    identifier,\n+                    default=None,\n+                )\n+                return user_order is not None\n             return False\n \n+        constraint = _get_with_identifier(\n+            self._constraints,\n+            identifier,\n+            default=Constraint.empty(),\n+        )\n         return self._factory.find_candidates(\n             identifier=identifier,\n             requirements=requirements,\n-            constraint=self._get_constraint(identifier),\n+            constraint=constraint,\n             prefers_installed=(not _eligible_for_upgrade(identifier)),\n             incompatibilities=incompatibilities,\n         )\n", "issue": "Package left un-upgraded with `pip install --upgrade`\n### Description\r\n\r\nWhen upgrading both `datasets` and `fsspec` in an environment, only `datasets` was upgraded to the latest version even though there was nothing constraining `fsspec` from being updated. `fsspec` is a dependency of `datasets` (`fsspec[http]>=2021.05.0` is specified in its [`setup.py`](https://github.com/huggingface/datasets/blob/1.14.0/setup.py)), and from looking at the resolver's debug output, it seems that pip is preferring to use the pre-installed version of `fsspec` instead of the newer one.\r\n\r\nThe documentation for `--upgrade` says, \"Upgrade all specified packages to the newest available version.\" Unless I'm misunderstanding, all packages explicitly listed on the command line should have been updated if possible.\r\n\r\n### Expected behavior\r\n\r\n`fsspec` should be upgraded to `2021.10.1`.\r\n\r\n### pip version\r\n\r\n21.3.1\r\n\r\n### Python version\r\n\r\n3.7.12\r\n\r\n### OS\r\n\r\nmacOS 10.15.7\r\n\r\n### How to Reproduce\r\n\r\n1. Create a file named `pip_fsspec_test.sh` with the following contents:\r\n\r\n```bash\r\n#!/usr/bin/env bash\r\n\r\nset -euxo pipefail\r\n\r\n# Prepare environment\r\npython --version\r\npip install -U pip setuptools wheel\r\npip list\r\n\r\n# Install outdated packages\r\npip install datasets==1.13.3 fsspec==2021.10.0\r\npip list --outdated\r\n\r\n# Attempt to upgrade both packages\r\npip install -U 'datasets<=1.14.0' 'fsspec<=2021.10.1'\r\npip list --outdated\r\n```\r\n\r\n2. Run `bash pip_fsspec_test.sh &> pip_fsspec_log.txt` in a new environment to create [pip_fsspec_log.txt](https://github.com/pypa/pip/files/7402239/pip_fsspec_log.txt).\r\n3. Change the penultimate line of the script to `PIP_RESOLVER_DEBUG=1 pip -vvv install -U 'datasets<=1.14.0' 'fsspec<=2021.10.1'`.\r\n4. Run `bash pip_fsspec_test.sh &> pip_fsspec_verbose_log.txt` in a new environment to create [pip_fsspec_verbose_log.txt](https://github.com/pypa/pip/files/7402240/pip_fsspec_verbose_log.txt).\r\n\r\n### Output\r\n\r\n_See files under \"How to Reproduce\"._\r\n\r\n### Code of Conduct\r\n\r\n- [X] I agree to follow the [PSF Code of Conduct](https://www.python.org/psf/conduct/).\n", "before_files": [{"content": "import collections\nimport math\nfrom typing import TYPE_CHECKING, Dict, Iterable, Iterator, Mapping, Sequence, Union\n\nfrom pip._vendor.resolvelib.providers import AbstractProvider\n\nfrom .base import Candidate, Constraint, Requirement\nfrom .candidates import REQUIRES_PYTHON_IDENTIFIER\nfrom .factory import Factory\n\nif TYPE_CHECKING:\n    from pip._vendor.resolvelib.providers import Preference\n    from pip._vendor.resolvelib.resolvers import RequirementInformation\n\n    PreferenceInformation = RequirementInformation[Requirement, Candidate]\n\n    _ProviderBase = AbstractProvider[Requirement, Candidate, str]\nelse:\n    _ProviderBase = AbstractProvider\n\n# Notes on the relationship between the provider, the factory, and the\n# candidate and requirement classes.\n#\n# The provider is a direct implementation of the resolvelib class. Its role\n# is to deliver the API that resolvelib expects.\n#\n# Rather than work with completely abstract \"requirement\" and \"candidate\"\n# concepts as resolvelib does, pip has concrete classes implementing these two\n# ideas. The API of Requirement and Candidate objects are defined in the base\n# classes, but essentially map fairly directly to the equivalent provider\n# methods. In particular, `find_matches` and `is_satisfied_by` are\n# requirement methods, and `get_dependencies` is a candidate method.\n#\n# The factory is the interface to pip's internal mechanisms. It is stateless,\n# and is created by the resolver and held as a property of the provider. It is\n# responsible for creating Requirement and Candidate objects, and provides\n# services to those objects (access to pip's finder and preparer).\n\n\nclass PipProvider(_ProviderBase):\n    \"\"\"Pip's provider implementation for resolvelib.\n\n    :params constraints: A mapping of constraints specified by the user. Keys\n        are canonicalized project names.\n    :params ignore_dependencies: Whether the user specified ``--no-deps``.\n    :params upgrade_strategy: The user-specified upgrade strategy.\n    :params user_requested: A set of canonicalized package names that the user\n        supplied for pip to install/upgrade.\n    \"\"\"\n\n    def __init__(\n        self,\n        factory: Factory,\n        constraints: Dict[str, Constraint],\n        ignore_dependencies: bool,\n        upgrade_strategy: str,\n        user_requested: Dict[str, int],\n    ) -> None:\n        self._factory = factory\n        self._constraints = constraints\n        self._ignore_dependencies = ignore_dependencies\n        self._upgrade_strategy = upgrade_strategy\n        self._user_requested = user_requested\n        self._known_depths: Dict[str, float] = collections.defaultdict(lambda: math.inf)\n\n    def identify(self, requirement_or_candidate: Union[Requirement, Candidate]) -> str:\n        return requirement_or_candidate.name\n\n    def get_preference(  # type: ignore\n        self,\n        identifier: str,\n        resolutions: Mapping[str, Candidate],\n        candidates: Mapping[str, Iterator[Candidate]],\n        information: Mapping[str, Iterable[\"PreferenceInformation\"]],\n        backtrack_causes: Sequence[\"PreferenceInformation\"],\n    ) -> \"Preference\":\n        \"\"\"Produce a sort key for given requirement based on preference.\n\n        The lower the return value is, the more preferred this group of\n        arguments is.\n\n        Currently pip considers the followings in order:\n\n        * Prefer if any of the known requirements is \"direct\", e.g. points to an\n          explicit URL.\n        * If equal, prefer if any requirement is \"pinned\", i.e. contains\n          operator ``===`` or ``==``.\n        * If equal, calculate an approximate \"depth\" and resolve requirements\n          closer to the user-specified requirements first.\n        * Order user-specified requirements by the order they are specified.\n        * If equal, prefers \"non-free\" requirements, i.e. contains at least one\n          operator, such as ``>=`` or ``<``.\n        * If equal, order alphabetically for consistency (helps debuggability).\n        \"\"\"\n        lookups = (r.get_candidate_lookup() for r, _ in information[identifier])\n        candidate, ireqs = zip(*lookups)\n        operators = [\n            specifier.operator\n            for specifier_set in (ireq.specifier for ireq in ireqs if ireq)\n            for specifier in specifier_set\n        ]\n\n        direct = candidate is not None\n        pinned = any(op[:2] == \"==\" for op in operators)\n        unfree = bool(operators)\n\n        try:\n            requested_order: Union[int, float] = self._user_requested[identifier]\n        except KeyError:\n            requested_order = math.inf\n            parent_depths = (\n                self._known_depths[parent.name] if parent is not None else 0.0\n                for _, parent in information[identifier]\n            )\n            inferred_depth = min(d for d in parent_depths) + 1.0\n        else:\n            inferred_depth = 1.0\n        self._known_depths[identifier] = inferred_depth\n\n        requested_order = self._user_requested.get(identifier, math.inf)\n\n        # Requires-Python has only one candidate and the check is basically\n        # free, so we always do it first to avoid needless work if it fails.\n        requires_python = identifier == REQUIRES_PYTHON_IDENTIFIER\n\n        # HACK: Setuptools have a very long and solid backward compatibility\n        # track record, and extremely few projects would request a narrow,\n        # non-recent version range of it since that would break a lot things.\n        # (Most projects specify it only to request for an installer feature,\n        # which does not work, but that's another topic.) Intentionally\n        # delaying Setuptools helps reduce branches the resolver has to check.\n        # This serves as a temporary fix for issues like \"apache-airflow[all]\"\n        # while we work on \"proper\" branch pruning techniques.\n        delay_this = identifier == \"setuptools\"\n\n        # Prefer the causes of backtracking on the assumption that the problem\n        # resolving the dependency tree is related to the failures that caused\n        # the backtracking\n        backtrack_cause = self.is_backtrack_cause(identifier, backtrack_causes)\n\n        return (\n            not requires_python,\n            delay_this,\n            not direct,\n            not pinned,\n            not backtrack_cause,\n            inferred_depth,\n            requested_order,\n            not unfree,\n            identifier,\n        )\n\n    def _get_constraint(self, identifier: str) -> Constraint:\n        if identifier in self._constraints:\n            return self._constraints[identifier]\n\n        # HACK: Theoretically we should check whether this identifier is a valid\n        # \"NAME[EXTRAS]\" format, and parse out the name part with packaging or\n        # some regular expression. But since pip's resolver only spits out\n        # three kinds of identifiers: normalized PEP 503 names, normalized names\n        # plus extras, and Requires-Python, we can cheat a bit here.\n        name, open_bracket, _ = identifier.partition(\"[\")\n        if open_bracket and name in self._constraints:\n            return self._constraints[name]\n\n        return Constraint.empty()\n\n    def find_matches(\n        self,\n        identifier: str,\n        requirements: Mapping[str, Iterator[Requirement]],\n        incompatibilities: Mapping[str, Iterator[Candidate]],\n    ) -> Iterable[Candidate]:\n        def _eligible_for_upgrade(name: str) -> bool:\n            \"\"\"Are upgrades allowed for this project?\n\n            This checks the upgrade strategy, and whether the project was one\n            that the user specified in the command line, in order to decide\n            whether we should upgrade if there's a newer version available.\n\n            (Note that we don't need access to the `--upgrade` flag, because\n            an upgrade strategy of \"to-satisfy-only\" means that `--upgrade`\n            was not specified).\n            \"\"\"\n            if self._upgrade_strategy == \"eager\":\n                return True\n            elif self._upgrade_strategy == \"only-if-needed\":\n                return name in self._user_requested\n            return False\n\n        return self._factory.find_candidates(\n            identifier=identifier,\n            requirements=requirements,\n            constraint=self._get_constraint(identifier),\n            prefers_installed=(not _eligible_for_upgrade(identifier)),\n            incompatibilities=incompatibilities,\n        )\n\n    def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> bool:\n        return requirement.is_satisfied_by(candidate)\n\n    def get_dependencies(self, candidate: Candidate) -> Sequence[Requirement]:\n        with_requires = not self._ignore_dependencies\n        return [r for r in candidate.iter_dependencies(with_requires) if r is not None]\n\n    @staticmethod\n    def is_backtrack_cause(\n        identifier: str, backtrack_causes: Sequence[\"PreferenceInformation\"]\n    ) -> bool:\n        for backtrack_cause in backtrack_causes:\n            if identifier == backtrack_cause.requirement.name:\n                return True\n            if backtrack_cause.parent and identifier == backtrack_cause.parent.name:\n                return True\n        return False\n", "path": "src/pip/_internal/resolution/resolvelib/provider.py"}], "after_files": [{"content": "import collections\nimport math\nfrom typing import (\n    TYPE_CHECKING,\n    Dict,\n    Iterable,\n    Iterator,\n    Mapping,\n    Sequence,\n    TypeVar,\n    Union,\n)\n\nfrom pip._vendor.resolvelib.providers import AbstractProvider\n\nfrom .base import Candidate, Constraint, Requirement\nfrom .candidates import REQUIRES_PYTHON_IDENTIFIER\nfrom .factory import Factory\n\nif TYPE_CHECKING:\n    from pip._vendor.resolvelib.providers import Preference\n    from pip._vendor.resolvelib.resolvers import RequirementInformation\n\n    PreferenceInformation = RequirementInformation[Requirement, Candidate]\n\n    _ProviderBase = AbstractProvider[Requirement, Candidate, str]\nelse:\n    _ProviderBase = AbstractProvider\n\n# Notes on the relationship between the provider, the factory, and the\n# candidate and requirement classes.\n#\n# The provider is a direct implementation of the resolvelib class. Its role\n# is to deliver the API that resolvelib expects.\n#\n# Rather than work with completely abstract \"requirement\" and \"candidate\"\n# concepts as resolvelib does, pip has concrete classes implementing these two\n# ideas. The API of Requirement and Candidate objects are defined in the base\n# classes, but essentially map fairly directly to the equivalent provider\n# methods. In particular, `find_matches` and `is_satisfied_by` are\n# requirement methods, and `get_dependencies` is a candidate method.\n#\n# The factory is the interface to pip's internal mechanisms. It is stateless,\n# and is created by the resolver and held as a property of the provider. It is\n# responsible for creating Requirement and Candidate objects, and provides\n# services to those objects (access to pip's finder and preparer).\n\n\nD = TypeVar(\"D\")\nV = TypeVar(\"V\")\n\n\ndef _get_with_identifier(\n    mapping: Mapping[str, V],\n    identifier: str,\n    default: D,\n) -> Union[D, V]:\n    \"\"\"Get item from a package name lookup mapping with a resolver identifier.\n\n    This extra logic is needed when the target mapping is keyed by package\n    name, which cannot be directly looked up with an identifier (which may\n    contain requested extras). Additional logic is added to also look up a value\n    by \"cleaning up\" the extras from the identifier.\n    \"\"\"\n    if identifier in mapping:\n        return mapping[identifier]\n    # HACK: Theoretically we should check whether this identifier is a valid\n    # \"NAME[EXTRAS]\" format, and parse out the name part with packaging or\n    # some regular expression. But since pip's resolver only spits out three\n    # kinds of identifiers: normalized PEP 503 names, normalized names plus\n    # extras, and Requires-Python, we can cheat a bit here.\n    name, open_bracket, _ = identifier.partition(\"[\")\n    if open_bracket and name in mapping:\n        return mapping[name]\n    return default\n\n\nclass PipProvider(_ProviderBase):\n    \"\"\"Pip's provider implementation for resolvelib.\n\n    :params constraints: A mapping of constraints specified by the user. Keys\n        are canonicalized project names.\n    :params ignore_dependencies: Whether the user specified ``--no-deps``.\n    :params upgrade_strategy: The user-specified upgrade strategy.\n    :params user_requested: A set of canonicalized package names that the user\n        supplied for pip to install/upgrade.\n    \"\"\"\n\n    def __init__(\n        self,\n        factory: Factory,\n        constraints: Dict[str, Constraint],\n        ignore_dependencies: bool,\n        upgrade_strategy: str,\n        user_requested: Dict[str, int],\n    ) -> None:\n        self._factory = factory\n        self._constraints = constraints\n        self._ignore_dependencies = ignore_dependencies\n        self._upgrade_strategy = upgrade_strategy\n        self._user_requested = user_requested\n        self._known_depths: Dict[str, float] = collections.defaultdict(lambda: math.inf)\n\n    def identify(self, requirement_or_candidate: Union[Requirement, Candidate]) -> str:\n        return requirement_or_candidate.name\n\n    def get_preference(  # type: ignore\n        self,\n        identifier: str,\n        resolutions: Mapping[str, Candidate],\n        candidates: Mapping[str, Iterator[Candidate]],\n        information: Mapping[str, Iterable[\"PreferenceInformation\"]],\n        backtrack_causes: Sequence[\"PreferenceInformation\"],\n    ) -> \"Preference\":\n        \"\"\"Produce a sort key for given requirement based on preference.\n\n        The lower the return value is, the more preferred this group of\n        arguments is.\n\n        Currently pip considers the followings in order:\n\n        * Prefer if any of the known requirements is \"direct\", e.g. points to an\n          explicit URL.\n        * If equal, prefer if any requirement is \"pinned\", i.e. contains\n          operator ``===`` or ``==``.\n        * If equal, calculate an approximate \"depth\" and resolve requirements\n          closer to the user-specified requirements first.\n        * Order user-specified requirements by the order they are specified.\n        * If equal, prefers \"non-free\" requirements, i.e. contains at least one\n          operator, such as ``>=`` or ``<``.\n        * If equal, order alphabetically for consistency (helps debuggability).\n        \"\"\"\n        lookups = (r.get_candidate_lookup() for r, _ in information[identifier])\n        candidate, ireqs = zip(*lookups)\n        operators = [\n            specifier.operator\n            for specifier_set in (ireq.specifier for ireq in ireqs if ireq)\n            for specifier in specifier_set\n        ]\n\n        direct = candidate is not None\n        pinned = any(op[:2] == \"==\" for op in operators)\n        unfree = bool(operators)\n\n        try:\n            requested_order: Union[int, float] = self._user_requested[identifier]\n        except KeyError:\n            requested_order = math.inf\n            parent_depths = (\n                self._known_depths[parent.name] if parent is not None else 0.0\n                for _, parent in information[identifier]\n            )\n            inferred_depth = min(d for d in parent_depths) + 1.0\n        else:\n            inferred_depth = 1.0\n        self._known_depths[identifier] = inferred_depth\n\n        requested_order = self._user_requested.get(identifier, math.inf)\n\n        # Requires-Python has only one candidate and the check is basically\n        # free, so we always do it first to avoid needless work if it fails.\n        requires_python = identifier == REQUIRES_PYTHON_IDENTIFIER\n\n        # HACK: Setuptools have a very long and solid backward compatibility\n        # track record, and extremely few projects would request a narrow,\n        # non-recent version range of it since that would break a lot things.\n        # (Most projects specify it only to request for an installer feature,\n        # which does not work, but that's another topic.) Intentionally\n        # delaying Setuptools helps reduce branches the resolver has to check.\n        # This serves as a temporary fix for issues like \"apache-airflow[all]\"\n        # while we work on \"proper\" branch pruning techniques.\n        delay_this = identifier == \"setuptools\"\n\n        # Prefer the causes of backtracking on the assumption that the problem\n        # resolving the dependency tree is related to the failures that caused\n        # the backtracking\n        backtrack_cause = self.is_backtrack_cause(identifier, backtrack_causes)\n\n        return (\n            not requires_python,\n            delay_this,\n            not direct,\n            not pinned,\n            not backtrack_cause,\n            inferred_depth,\n            requested_order,\n            not unfree,\n            identifier,\n        )\n\n    def find_matches(\n        self,\n        identifier: str,\n        requirements: Mapping[str, Iterator[Requirement]],\n        incompatibilities: Mapping[str, Iterator[Candidate]],\n    ) -> Iterable[Candidate]:\n        def _eligible_for_upgrade(identifier: str) -> bool:\n            \"\"\"Are upgrades allowed for this project?\n\n            This checks the upgrade strategy, and whether the project was one\n            that the user specified in the command line, in order to decide\n            whether we should upgrade if there's a newer version available.\n\n            (Note that we don't need access to the `--upgrade` flag, because\n            an upgrade strategy of \"to-satisfy-only\" means that `--upgrade`\n            was not specified).\n            \"\"\"\n            if self._upgrade_strategy == \"eager\":\n                return True\n            elif self._upgrade_strategy == \"only-if-needed\":\n                user_order = _get_with_identifier(\n                    self._user_requested,\n                    identifier,\n                    default=None,\n                )\n                return user_order is not None\n            return False\n\n        constraint = _get_with_identifier(\n            self._constraints,\n            identifier,\n            default=Constraint.empty(),\n        )\n        return self._factory.find_candidates(\n            identifier=identifier,\n            requirements=requirements,\n            constraint=constraint,\n            prefers_installed=(not _eligible_for_upgrade(identifier)),\n            incompatibilities=incompatibilities,\n        )\n\n    def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> bool:\n        return requirement.is_satisfied_by(candidate)\n\n    def get_dependencies(self, candidate: Candidate) -> Sequence[Requirement]:\n        with_requires = not self._ignore_dependencies\n        return [r for r in candidate.iter_dependencies(with_requires) if r is not None]\n\n    @staticmethod\n    def is_backtrack_cause(\n        identifier: str, backtrack_causes: Sequence[\"PreferenceInformation\"]\n    ) -> bool:\n        for backtrack_cause in backtrack_causes:\n            if identifier == backtrack_cause.requirement.name:\n                return True\n            if backtrack_cause.parent and identifier == backtrack_cause.parent.name:\n                return True\n        return False\n", "path": "src/pip/_internal/resolution/resolvelib/provider.py"}]}