bertilhatt: Wasn’t able to test locally because of too many errors after cloning the repository; see https://github.com/pypa/pip/issues/5937.
benoit-pierre: You forgot a number or references to `SETUPTOOLS_SHIM`.
pradyunsg: ```
./src/pip/_internal/download.py:from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM
./src/pip/_internal/download.py:    sdist_args.append(SETUPTOOLS_SHIM % setup_py)
./src/pip/_internal/utils/setuptools_build.py:SETUPTOOLS_SHIM = (
./src/pip/_internal/req/req_install.py:from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM
./src/pip/_internal/req/req_install.py:            script = SETUPTOOLS_SHIM % self.setup_py
./src/pip/_internal/req/req_install.py:                        SETUPTOOLS_SHIM % self.setup_py
./src/pip/_internal/req/req_install.py:        install_args.append(SETUPTOOLS_SHIM % self.setup_py)
./src/pip/_internal/wheel.py:from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM
./src/pip/_internal/wheel.py:            SETUPTOOLS_SHIM % req.setup_py
```

BrownTruck: Hello!

I am an automated bot and I have noticed that this pull request is not currently able to be merged. If you are able to either merge the ``master`` branch into this pull request or rebase this pull request against ``master`` then it will be eligible for code review and hopefully merging!
pganssle: @bertilhatt @pypa/pip-committers I have rebased (and did some squashing along the way) this PR against the current master in [this branch on my fork](https://github.com/pganssle/pip/tree/subprocess_invocation_out_setuptools_args)

Could a maintainer force-push my changes to the original PR branch so that this can be merged? Here's how to do that:

```
git remote add bertilhatt git@github.com:bertilhatt/pip.git
git fetch bertilhatt
git checkout subprocess_invocation_out_setuptools_args
git remote add pganssle git@github.com:pganssle/pip.git
git fetch pganssle
git reset --hard pganssle/subprocess_invocation_out_setuptools_args
git push --force-with-lease
```

@bertilhatt If you want to do it yourself, you can skip the first two lines and start at `git checkout subprocess_invocation_out_setuptools_args`.
cjerdonek: @pganssle I did what you requested. Thanks!
pganssle: Ugh, I seem to have broken things in my rebase because I accidentally converted `setup_py` to `setup.py`.

@cjerdonek I've force-pushed a fixed version to my branch that hopefully fixes this. Sorry about that, you should be able to `fetch` my latest version and reset @bertilhatt's to my current head, which is commit [844f4f697f74575213ed786635c17de4fb66f350](https://github.com/pganssle/pip/commit/844f4f697f74575213ed786635c17de4fb66f350)
cjerdonek: Done.
BrownTruck: Hello!

I am an automated bot and I have noticed that this pull request is not currently able to be merged. If you are able to either merge the ``master`` branch into this pull request or rebase this pull request against ``master`` then it will be eligible for code review and hopefully merging!
cjerdonek: @pradyunsg @pganssle Could either of you take a look at this again before it's merged? It's been several months and a couple rebases before either of you gave the first okay.
pradyunsg: LGTM. @cjerdonek Let's merge if you think this is good to go. :)
diff --git a/docs/html/reference/pip_install.rst b/docs/html/reference/pip_install.rst
index 96fd1c018..a84f4746d 100644
--- a/docs/html/reference/pip_install.rst
+++ b/docs/html/reference/pip_install.rst
@@ -856,8 +856,8 @@ Examples
 
       $ pip install SomePackage[PDF]
       $ pip install git+https://git.repo/some_pkg.git#egg=SomePackage[PDF]
-      $ pip install .[PDF]  # project in current directory
       $ pip install SomePackage[PDF]==3.0
+      $ pip install -e .[PDF]==3.0  # editable project in current directory
       $ pip install SomePackage[PDF,EPUB]  # multiple extras
 
 
diff --git a/news/1234.trivial b/news/1234.trivial
deleted file mode 100644
index e69de29bb..000000000
diff --git a/news/1890.bugfix b/news/1890.bugfix
new file mode 100644
index 000000000..8f85e1255
--- /dev/null
+++ b/news/1890.bugfix
@@ -0,0 +1,2 @@
+Set ``sys.argv[0]`` to the underlying ``setup.py`` when invoking ``setup.py``
+via the setuptools shim so setuptools doesn't think the path is ``-c``.
diff --git a/news/4733.doc b/news/4733.doc
deleted file mode 100644
index 958736248..000000000
--- a/news/4733.doc
+++ /dev/null
@@ -1,1 +0,0 @@
-Replace a failing example of pip installs with extras with a working one.
diff --git a/news/5671.bugfix b/news/5671.bugfix
deleted file mode 100644
index 1dfa6c394..000000000
--- a/news/5671.bugfix
+++ /dev/null
@@ -1,1 +0,0 @@
-Make the "ascii" progress bar really be "ascii" and not Unicode.
diff --git a/src/pip/_internal/index.py b/src/pip/_internal/index.py
index e0e78678c..247de0ce4 100644
--- a/src/pip/_internal/index.py
+++ b/src/pip/_internal/index.py
@@ -7,6 +7,7 @@ import logging
 import mimetypes
 import os
 import re
+from collections import namedtuple
 
 from pip._vendor import html5lib, requests, six
 from pip._vendor.distlib.compat import unescape
@@ -40,8 +41,8 @@ from pip._internal.wheel import Wheel
 if MYPY_CHECK_RUNNING:
     from logging import Logger
     from typing import (
-        Any, Callable, FrozenSet, Iterable, Iterator, List, MutableMapping,
-        Optional, Sequence, Set, Text, Tuple, Union,
+        Any, Callable, Iterable, Iterator, List, MutableMapping, Optional,
+        Sequence, Set, Text, Tuple, Union,
     )
     import xml.etree.ElementTree
     from pip._vendor.packaging.version import _BaseVersion
@@ -49,7 +50,6 @@ if MYPY_CHECK_RUNNING:
     from pip._internal.models.search_scope import SearchScope
     from pip._internal.req import InstallRequirement
     from pip._internal.download import PipSession
-    from pip._internal.pep425tags import Pep425Tag
 
     BuildTag = Tuple[Any, ...]  # either empty tuple or Tuple[int, str]
     CandidateSortingKey = (
@@ -301,58 +301,62 @@ def _check_link_requires_python(
     return True
 
 
-class LinkEvaluator(object):
+class CandidateEvaluator(object):
 
     """
-    Responsible for evaluating links for a particular project.
+    Responsible for filtering and sorting candidates for installation based
+    on what tags are valid.
     """
 
-    _py_version_re = re.compile(r'-py([123]\.?[0-9]?)$')
-
     # Don't include an allow_yanked default value to make sure each call
     # site considers whether yanked releases are allowed. This also causes
     # that decision to be made explicit in the calling code, which helps
     # people when reading the code.
     def __init__(
         self,
-        project_name,    # type: str
-        canonical_name,  # type: str
-        formats,         # type: FrozenSet
-        target_python,   # type: TargetPython
-        allow_yanked,    # type: bool
+        allow_yanked,  # type: bool
+        target_python=None,  # type: Optional[TargetPython]
+        prefer_binary=False,   # type: bool
+        allow_all_prereleases=False,  # type: bool
         ignore_requires_python=None,  # type: Optional[bool]
     ):
         # type: (...) -> None
         """
-        :param project_name: The user supplied package name.
-        :param canonical_name: The canonical package name.
-        :param formats: The formats allowed for this package. Should be a set
-            with 'binary' or 'source' or both in it.
-        :param target_python: The target Python interpreter to use when
-            evaluating link compatibility. This is used, for example, to
-            check wheel compatibility, as well as when checking the Python
-            version, e.g. the Python version embedded in a link filename
-            (or egg fragment) and against an HTML link's optional PEP 503
-            "data-requires-python" attribute.
         :param allow_yanked: Whether files marked as yanked (in the sense
             of PEP 592) are permitted to be candidates for install.
+        :param target_python: The target Python interpreter to use to check
+            both the Python version embedded in the filename and the package's
+            "Requires-Python" metadata. If None (the default), then a
+            TargetPython object will be constructed from the running Python.
+        :param allow_all_prereleases: Whether to allow all pre-releases.
         :param ignore_requires_python: Whether to ignore incompatible
-            PEP 503 "data-requires-python" values in HTML links. Defaults
-            to False.
+            "Requires-Python" values in links. Defaults to False.
         """
+        if target_python is None:
+            target_python = TargetPython()
         if ignore_requires_python is None:
             ignore_requires_python = False
 
         self._allow_yanked = allow_yanked
-        self._canonical_name = canonical_name
         self._ignore_requires_python = ignore_requires_python
-        self._formats = formats
+        self._prefer_binary = prefer_binary
         self._target_python = target_python
 
-        self.project_name = project_name
+        # We compile the regex here instead of as a class attribute so as
+        # not to impact pip start-up time.  This is also okay because
+        # CandidateEvaluator is generally instantiated only once per pip
+        # invocation (when PackageFinder is instantiated).
+        self._py_version_re = re.compile(r'-py([123]\.?[0-9]?)$')
+
+        self.allow_all_prereleases = allow_all_prereleases
 
-    def evaluate_link(self, link):
-        # type: (Link) -> Tuple[bool, Optional[Text]]
+    def _is_wheel_supported(self, wheel):
+        # type: (Wheel) -> bool
+        valid_tags = self._target_python.get_tags()
+        return wheel.supported(valid_tags)
+
+    def evaluate_link(self, link, search):
+        # type: (Link, Search) -> Tuple[bool, Optional[Text]]
         """
         Determine whether a link is a candidate for installation.
 
@@ -378,8 +382,8 @@ class LinkEvaluator(object):
                 return (False, 'not a file')
             if ext not in SUPPORTED_EXTENSIONS:
                 return (False, 'unsupported archive format: %s' % ext)
-            if "binary" not in self._formats and ext == WHEEL_EXTENSION:
-                reason = 'No binaries permitted for %s' % self.project_name
+            if "binary" not in search.formats and ext == WHEEL_EXTENSION:
+                reason = 'No binaries permitted for %s' % search.supplied
                 return (False, reason)
             if "macosx10" in link.path and ext == '.zip':
                 return (False, 'macosx10 one')
@@ -388,12 +392,11 @@ class LinkEvaluator(object):
                     wheel = Wheel(link.filename)
                 except InvalidWheelFilename:
                     return (False, 'invalid wheel filename')
-                if canonicalize_name(wheel.name) != self._canonical_name:
-                    reason = 'wrong project name (not %s)' % self.project_name
+                if canonicalize_name(wheel.name) != search.canonical:
+                    reason = 'wrong project name (not %s)' % search.supplied
                     return (False, reason)
 
-                supported_tags = self._target_python.get_tags()
-                if not wheel.supported(supported_tags):
+                if not self._is_wheel_supported(wheel):
                     # Include the wheel's tags in the reason string to
                     # simplify troubleshooting compatibility issues.
                     file_tags = wheel.get_formatted_file_tags()
@@ -406,18 +409,16 @@ class LinkEvaluator(object):
 
                 version = wheel.version
 
-        # This should be up by the self.ok_binary check, but see issue 2700.
-        if "source" not in self._formats and ext != WHEEL_EXTENSION:
-            return (False, 'No sources permitted for %s' % self.project_name)
+        # This should be up by the search.ok_binary check, but see issue 2700.
+        if "source" not in search.formats and ext != WHEEL_EXTENSION:
+            return (False, 'No sources permitted for %s' % search.supplied)
 
         if not version:
             version = _extract_version_from_fragment(
-                egg_info, self._canonical_name,
+                egg_info, search.canonical,
             )
         if not version:
-            return (
-                False, 'Missing project version for %s' % self.project_name,
-            )
+            return (False, 'Missing project version for %s' % search.supplied)
 
         match = self._py_version_re.search(version)
         if match:
@@ -439,36 +440,6 @@ class LinkEvaluator(object):
 
         return (True, version)
 
-
-class CandidateEvaluator(object):
-
-    """
-    Responsible for filtering and sorting candidates for installation based
-    on what tags are valid.
-    """
-
-    def __init__(
-        self,
-        supported_tags=None,  # type: Optional[List[Pep425Tag]]
-        prefer_binary=False,  # type: bool
-        allow_all_prereleases=False,  # type: bool
-    ):
-        # type: (...) -> None
-        """
-        :param supported_tags: The PEP 425 tags supported by the target
-            Python in order of preference (most preferred first). If None,
-            then the list will be generated from the running Python.
-        :param allow_all_prereleases: Whether to allow all pre-releases.
-        """
-        if supported_tags is None:
-            target_python = TargetPython()
-            supported_tags = target_python.get_tags()
-
-        self._prefer_binary = prefer_binary
-        self._supported_tags = supported_tags
-
-        self.allow_all_prereleases = allow_all_prereleases
-
     def make_found_candidates(
         self,
         candidates,      # type: List[InstallationCandidate]
@@ -500,17 +471,7 @@ class CandidateEvaluator(object):
                 prereleases=allow_prereleases,
             )
         }
-
-        # Again, converting version to str to deal with debundling.
-        applicable_candidates = [
-            c for c in candidates if str(c.version) in versions
-        ]
-
-        return FoundCandidates(
-            candidates,
-            applicable_candidates=applicable_candidates,
-            evaluator=self,
-        )
+        return FoundCandidates(candidates, versions=versions, evaluator=self)
 
     def _sort_key(self, candidate):
         # type: (InstallationCandidate) -> CandidateSortingKey
@@ -529,7 +490,7 @@ class CandidateEvaluator(object):
         If not finding wheels, they are sorted by version only.
         If finding wheels, then the sort order is by version, then:
           1. existing installs
-          2. wheels ordered via Wheel.support_index_min(self._supported_tags)
+          2. wheels ordered via Wheel.support_index_min(self._valid_tags)
           3. source archives
         If prefer_binary was set, then all wheels are sorted above sources.
 
@@ -537,7 +498,7 @@ class CandidateEvaluator(object):
               comparison operators, but then different sdist links
               with the same version, would have to be considered equal
         """
-        valid_tags = self._supported_tags
+        valid_tags = self._target_python.get_tags()
         support_num = len(valid_tags)
         build_tag = tuple()  # type: BuildTag
         binary_preference = 0
@@ -545,7 +506,7 @@ class CandidateEvaluator(object):
         if link.is_wheel:
             # can raise InvalidWheelFilename
             wheel = Wheel(link.filename)
-            if not wheel.supported(valid_tags):
+            if not self._is_wheel_supported(wheel):
                 raise UnsupportedWheel(
                     "%s is not a supported wheel for this platform. It "
                     "can't be sorted." % wheel.filename
@@ -604,20 +565,21 @@ class FoundCandidates(object):
 
     def __init__(
         self,
-        candidates,             # type: List[InstallationCandidate]
-        applicable_candidates,  # type: List[InstallationCandidate]
-        evaluator,              # type: CandidateEvaluator
+        candidates,     # type: List[InstallationCandidate]
+        versions,       # type: Set[str]
+        evaluator,      # type: CandidateEvaluator
     ):
         # type: (...) -> None
         """
         :param candidates: A sequence of all available candidates found.
-        :param applicable_candidates: The applicable candidates.
+        :param versions: The applicable versions to filter applicable
+            candidates.
         :param evaluator: A CandidateEvaluator object to sort applicable
             candidates by order of preference.
         """
-        self._applicable_candidates = applicable_candidates
         self._candidates = candidates
         self._evaluator = evaluator
+        self._versions = versions
 
     def iter_all(self):
         # type: () -> Iterable[InstallationCandidate]
@@ -627,9 +589,11 @@ class FoundCandidates(object):
 
     def iter_applicable(self):
         # type: () -> Iterable[InstallationCandidate]
-        """Iterate through the applicable candidates.
+        """Iterate through candidates matching the versions associated with
+        this instance.
         """
-        return iter(self._applicable_candidates)
+        # Again, converting version to str to deal with debundling.
+        return (c for c in self.iter_all() if str(c.version) in self._versions)
 
     def get_best(self):
         # type: () -> Optional[InstallationCandidate]
@@ -652,11 +616,8 @@ class PackageFinder(object):
         candidate_evaluator,  # type: CandidateEvaluator
         search_scope,         # type: SearchScope
         session,  # type: PipSession
-        target_python,        # type: TargetPython
-        allow_yanked,         # type: bool
         format_control=None,  # type: Optional[FormatControl]
         trusted_hosts=None,   # type: Optional[List[str]]
-        ignore_requires_python=None,  # type: Optional[bool]
     ):
         # type: (...) -> None
         """
@@ -674,10 +635,6 @@ class PackageFinder(object):
 
         format_control = format_control or FormatControl(set(), set())
 
-        self._allow_yanked = allow_yanked
-        self._ignore_requires_python = ignore_requires_python
-        self._target_python = target_python
-
         self.candidate_evaluator = candidate_evaluator
         self.search_scope = search_scope
         self.session = session
@@ -708,34 +665,28 @@ class PackageFinder(object):
         :param trusted_hosts: Domains not to emit warnings for when not using
             HTTPS.
         :param session: The Session to use to make requests.
-        :param target_python: The target Python interpreter to use when
-            checking compatibility. If None (the default), a TargetPython
-            object will be constructed from the running Python.
+        :param target_python: The target Python interpreter.
         """
         if session is None:
             raise TypeError(
                 "PackageFinder.create() missing 1 required keyword argument: "
                 "'session'"
             )
-        if target_python is None:
-            target_python = TargetPython()
 
-        supported_tags = target_python.get_tags()
         candidate_evaluator = CandidateEvaluator(
-            supported_tags=supported_tags,
+            allow_yanked=selection_prefs.allow_yanked,
+            target_python=target_python,
             prefer_binary=selection_prefs.prefer_binary,
             allow_all_prereleases=selection_prefs.allow_all_prereleases,
+            ignore_requires_python=selection_prefs.ignore_requires_python,
         )
 
         return cls(
             candidate_evaluator=candidate_evaluator,
             search_scope=search_scope,
             session=session,
-            target_python=target_python,
-            allow_yanked=selection_prefs.allow_yanked,
             format_control=selection_prefs.format_control,
             trusted_hosts=trusted_hosts,
-            ignore_requires_python=selection_prefs.ignore_requires_python,
         )
 
     @property
@@ -914,20 +865,6 @@ class PackageFinder(object):
 
         return False
 
-    def make_link_evaluator(self, project_name):
-        # type: (str) -> LinkEvaluator
-        canonical_name = canonicalize_name(project_name)
-        formats = self.format_control.get_allowed_formats(canonical_name)
-
-        return LinkEvaluator(
-            project_name=project_name,
-            canonical_name=canonical_name,
-            formats=formats,
-            target_python=self._target_python,
-            allow_yanked=self._allow_yanked,
-            ignore_requires_python=self._ignore_requires_python,
-        )
-
     def find_all_candidates(self, project_name):
         # type: (str) -> List[InstallationCandidate]
         """Find all available InstallationCandidate for project_name
@@ -935,7 +872,7 @@ class PackageFinder(object):
         This checks index_urls and find_links.
         All versions found are returned as an InstallationCandidate list.
 
-        See LinkEvaluator.evaluate_link() for details on which files
+        See CandidateEvaluator.evaluate_link() for details on which files
         are accepted.
         """
         search_scope = self.search_scope
@@ -966,11 +903,13 @@ class PackageFinder(object):
         for location in url_locations:
             logger.debug('* %s', location)
 
-        link_evaluator = self.make_link_evaluator(project_name)
+        canonical_name = canonicalize_name(project_name)
+        formats = self.format_control.get_allowed_formats(canonical_name)
+        search = Search(project_name, canonical_name, formats)
         find_links_versions = self._package_versions(
-            link_evaluator,
             # We trust every directly linked archive in find_links
             (Link(url, '-f') for url in self.find_links),
+            search
         )
 
         page_versions = []
@@ -978,10 +917,10 @@ class PackageFinder(object):
             logger.debug('Analyzing links from page %s', page.url)
             with indent_log():
                 page_versions.extend(
-                    self._package_versions(link_evaluator, page.iter_links())
+                    self._package_versions(page.iter_links(), search)
                 )
 
-        file_versions = self._package_versions(link_evaluator, file_locations)
+        file_versions = self._package_versions(file_locations, search)
         if file_versions:
             file_versions.sort(reverse=True)
             logger.debug(
@@ -1136,31 +1075,35 @@ class PackageFinder(object):
             logger.debug(u'Skipping link: %s: %s', reason, link)
             self._logged_links.add(link)
 
-    def get_install_candidate(self, link_evaluator, link):
-        # type: (LinkEvaluator, Link) -> Optional[InstallationCandidate]
+    def get_install_candidate(self, link, search):
+        # type: (Link, Search) -> Optional[InstallationCandidate]
         """
         If the link is a candidate for install, convert it to an
         InstallationCandidate and return it. Otherwise, return None.
         """
-        is_candidate, result = link_evaluator.evaluate_link(link)
+        is_candidate, result = (
+            self.candidate_evaluator.evaluate_link(link, search=search)
+        )
         if not is_candidate:
             if result:
                 self._log_skipped_link(link, reason=result)
             return None
 
         return InstallationCandidate(
-            project=link_evaluator.project_name,
-            location=link,
             # Convert the Text result to str since InstallationCandidate
             # accepts str.
-            version=str(result),
+            search.supplied, location=link, version=str(result),
         )
 
-    def _package_versions(self, link_evaluator, links):
-        # type: (LinkEvaluator, Iterable[Link]) -> List[InstallationCandidate]
+    def _package_versions(
+        self,
+        links,  # type: Iterable[Link]
+        search  # type: Search
+    ):
+        # type: (...) -> List[InstallationCandidate]
         result = []
         for link in self._sort_links(links):
-            candidate = self.get_install_candidate(link_evaluator, link)
+            candidate = self.get_install_candidate(link, search=search)
             if candidate is not None:
                 result.append(candidate)
         return result
@@ -1329,3 +1272,13 @@ class HTMLPage(object):
             if link is None:
                 continue
             yield link
+
+
+Search = namedtuple('Search', 'supplied canonical formats')
+"""Capture key aspects of a search.
+
+:attribute supplied: The user supplied package.
+:attribute canonical: The canonical package name.
+:attribute formats: The formats allowed for this package. Should be a set
+    with 'binary' or 'source' or both in it.
+"""
diff --git a/src/pip/_internal/models/target_python.py b/src/pip/_internal/models/target_python.py
index a23b79c4e..7ad5786c4 100644
--- a/src/pip/_internal/models/target_python.py
+++ b/src/pip/_internal/models/target_python.py
@@ -82,9 +82,7 @@ class TargetPython(object):
     def get_tags(self):
         # type: () -> List[Pep425Tag]
         """
-        Return the supported PEP 425 tags to check wheel candidates against.
-
-        The tags are returned in order of preference (most preferred first).
+        Return the supported tags to check wheel candidates against.
         """
         if self._valid_tags is None:
             # Pass versions=None if no py_version_info was given since
diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py
index 5192ab2fd..59305df02 100644
--- a/src/pip/_internal/req/req_install.py
+++ b/src/pip/_internal/req/req_install.py
@@ -595,7 +595,7 @@ class InstallRequirement(object):
                 'Running setup.py (path:%s) egg_info for package from %s',
                 self.setup_py_path, self.link,
             )
-        script = SETUPTOOLS_SHIM % self.setup_py_path
+        script = SETUPTOOLS_SHIM.format(self.setup_py_path)
         base_cmd = [sys.executable, '-c', script]
         if self.isolated:
             base_cmd += ["--no-user-cfg"]
@@ -757,7 +757,7 @@ class InstallRequirement(object):
                     [
                         sys.executable,
                         '-c',
-                        SETUPTOOLS_SHIM % self.setup_py_path
+                        SETUPTOOLS_SHIM.format(self.setup_py_path)
                     ] +
                     list(global_options) +
                     ['develop', '--no-deps'] +
@@ -1004,7 +1004,7 @@ class InstallRequirement(object):
         # type: (...) -> List[str]
         install_args = [sys.executable, "-u"]
         install_args.append('-c')
-        install_args.append(SETUPTOOLS_SHIM % self.setup_py_path)
+        install_args.append(SETUPTOOLS_SHIM.format(self.setup_py_path))
         install_args += list(global_options) + \
             ['install', '--record', record_filename]
         install_args += ['--single-version-externally-managed']
diff --git a/src/pip/_internal/utils/compat.py b/src/pip/_internal/utils/compat.py
index ec3995c29..0250c07ec 100644
--- a/src/pip/_internal/utils/compat.py
+++ b/src/pip/_internal/utils/compat.py
@@ -15,7 +15,7 @@ from pip._vendor.urllib3.util import IS_PYOPENSSL
 from pip._internal.utils.typing import MYPY_CHECK_RUNNING
 
 if MYPY_CHECK_RUNNING:
-    from typing import Optional, Text, Tuple, Union
+    from typing import Tuple, Text
 
 try:
     import _ssl  # noqa
@@ -83,29 +83,18 @@ else:
     backslashreplace_decode = "backslashreplace_decode"
 
 
-def str_to_display(data, desc=None):
-    # type: (Union[bytes, Text], Optional[str]) -> Text
-    """
-    For display or logging purposes, convert a bytes object (or text) to
-    text (e.g. unicode in Python 2) safe for output.
-
-    :param desc: An optional phrase describing the input data, for use in
-        the log message if a warning is logged. Defaults to "Bytes object".
-
-    This function should never error out and so can take a best effort
-    approach. It is okay to be lossy if needed since the return value is
-    just for display.
+def console_to_str(data):
+    # type: (bytes) -> Text
+    """Return a string, safe for output, of subprocess output.
 
-    We assume the data is in the locale preferred encoding. If it won't
-    decode properly, we warn the user but decode as best we can.
+    We assume the data is in the locale preferred encoding.
+    If it won't decode properly, we warn the user but decode as
+    best we can.
 
-    We also ensure that the output can be safely written to standard output
-    without encoding errors.
+    We also ensure that the output can be safely written to
+    standard output without encoding errors.
     """
-    if isinstance(data, text_type):
-        return data
 
-    # Otherwise, data is a bytes object (str in Python 2).
     # First, get the encoding we assume. This is the preferred
     # encoding for the locale, unless that is not found, or
     # it is ASCII, in which case assume UTF-8
@@ -118,10 +107,10 @@ def str_to_display(data, desc=None):
     try:
         decoded_data = data.decode(encoding)
     except UnicodeDecodeError:
-        if desc is None:
-            desc = 'Bytes object'
-        msg_format = '{} does not appear to be encoded as %s'.format(desc)
-        logger.warning(msg_format, encoding)
+        logger.warning(
+            "Subprocess output does not appear to be encoded as %s",
+            encoding,
+        )
         decoded_data = data.decode(encoding, errors=backslashreplace_decode)
 
     # Make sure we can print the output, by encoding it to the output
@@ -149,13 +138,6 @@ def str_to_display(data, desc=None):
     return decoded_data
 
 
-def console_to_str(data):
-    # type: (bytes) -> Text
-    """Return a string, safe for output, of subprocess output.
-    """
-    return str_to_display(data, desc='Subprocess output')
-
-
 if sys.version_info >= (3,):
     def native_str(s, replace=False):
         # type: (str, bool) -> str
diff --git a/src/pip/_internal/utils/misc.py b/src/pip/_internal/utils/misc.py
index f42fa5f74..c9d689453 100644
--- a/src/pip/_internal/utils/misc.py
+++ b/src/pip/_internal/utils/misc.py
@@ -35,7 +35,7 @@ from pip._internal.locations import (
     write_delete_marker_file,
 )
 from pip._internal.utils.compat import (
-    WINDOWS, console_to_str, expanduser, stdlib_pkgs, str_to_display,
+    WINDOWS, console_to_str, expanduser, stdlib_pkgs,
 )
 from pip._internal.utils.typing import MYPY_CHECK_RUNNING
 
@@ -751,25 +751,19 @@ def make_subprocess_output_error(
     :param lines: A list of lines, each ending with a newline.
     """
     command = format_command_args(cmd_args)
-    # Convert `command` to text (unicode in Python 2) so we can use it as
-    # an argument in the unicode format string below. This avoids
-    # "UnicodeDecodeError: 'ascii' codec can't decode byte ..." in Python 2
-    # when the formatted command contains a non-ascii character.
-    command_display = str_to_display(command, desc='command bytes')
-
     # We know the joined output value ends in a newline.
     output = ''.join(lines)
     msg = (
-        # Use a unicode string to avoid "UnicodeEncodeError: 'ascii'
-        # codec can't encode character ..." in Python 2 when a format
-        # argument (e.g. `output`) has a non-ascii character.
+        # We need to mark this explicitly as a unicode string to avoid
+        # "UnicodeEncodeError: 'ascii' codec can't encode character ..."
+        # errors in Python 2 since e.g. `output` is a unicode string.
         u'Command errored out with exit status {exit_status}:\n'
-        ' command: {command_display}\n'
+        ' command: {command}\n'
         '     cwd: {cwd}\n'
         'Complete output ({line_count} lines):\n{output}{divider}'
     ).format(
         exit_status=exit_status,
-        command_display=command_display,
+        command=command,
         cwd=cwd,
         line_count=len(lines),
         output=output,
diff --git a/src/pip/_internal/utils/setuptools_build.py b/src/pip/_internal/utils/setuptools_build.py
index 03973e976..207c6fb96 100644
--- a/src/pip/_internal/utils/setuptools_build.py
+++ b/src/pip/_internal/utils/setuptools_build.py
@@ -1,6 +1,11 @@
 # Shim to wrap setup.py invocation with setuptools
+#
+# We set sys.argv[0] to the path to the underlying setup.py file so
+# setuptools / distutils don't take the path to the setup.py to be "-c" when
+# invoking via the shim.  This avoids e.g. the following manifest_maker
+# warning: "warning: manifest_maker: standard file '-c' not found".
 SETUPTOOLS_SHIM = (
-    "import setuptools, tokenize;__file__=%r;"
+    "import sys, setuptools, tokenize; sys.argv[0] = {0!r}; __file__={0!r};"
     "f=getattr(tokenize, 'open', open)(__file__);"
     "code=f.read().replace('\\r\\n', '\\n');"
     "f.close();"
diff --git a/src/pip/_internal/utils/ui.py b/src/pip/_internal/utils/ui.py
index 46390f4a5..0902ce71e 100644
--- a/src/pip/_internal/utils/ui.py
+++ b/src/pip/_internal/utils/ui.py
@@ -208,8 +208,8 @@ class DownloadSilentBar(BaseDownloadProgressBar, SilentBar):  # type: ignore
     pass
 
 
-class DownloadBar(BaseDownloadProgressBar,  # type: ignore
-                  Bar):
+class DownloadIncrementalBar(BaseDownloadProgressBar,  # type: ignore
+                             IncrementalBar):
     pass
 
 
@@ -252,7 +252,7 @@ class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin,
 BAR_TYPES = {
     "off": (DownloadSilentBar, DownloadSilentBar),
     "on": (DefaultDownloadProgressBar, DownloadProgressSpinner),
-    "ascii": (DownloadBar, DownloadProgressSpinner),
+    "ascii": (DownloadIncrementalBar, DownloadProgressSpinner),
     "pretty": (DownloadFillingCirclesBar, DownloadProgressSpinner),
     "emoji": (DownloadBlueEmojiProgressBar, DownloadProgressSpinner)
 }
diff --git a/src/pip/_internal/wheel.py b/src/pip/_internal/wheel.py
index a7350c9ec..a3934acba 100644
--- a/src/pip/_internal/wheel.py
+++ b/src/pip/_internal/wheel.py
@@ -930,7 +930,7 @@ class WheelBuilder(object):
         # virtualenv.
         return [
             sys.executable, '-u', '-c',
-            SETUPTOOLS_SHIM % req.setup_py_path,
+            SETUPTOOLS_SHIM.format(req.setup_py_path)
         ] + list(self.global_options)
 
     def _build_one_pep517(self, req, tempd, python_tag=None):

python subprocess invocation to inject setuptools pollutes setuptools argument list
setuptools (and probably distutils) assume the very first argument from sys.argv to be the executed setup file. Invoking a python subprocess with -c to execute a command will set '-c' to be the very first argument to sys.argv (see the docs). This leads to the manifest_maker warning noted at https://github.com/pypa/pip/blob/9b11ed231cc0c7a8101f1ffbdc8b352a12c511b7/pip/req/req_install.py#L868 and also will cause arguments to the setuptools (or distutils) commands to be polluted with an additional '-c', whether it's wanted or not.

As a workaround, manipulate the argument list prior to exec() in https://github.com/pypa/pip/blob/9b11ed231cc0c7a8101f1ffbdc8b352a12c511b7/pip/req/req_install.py#L843 and similar invocations within pip:

```
import sys, setuptools, tokenize
sys.argv[0] = %r; ....
```

diff --git a/tests/unit/test_compat.py b/tests/unit/test_compat.py
index 6f5fe5b51..5f9cd375e 100644
--- a/tests/unit/test_compat.py
+++ b/tests/unit/test_compat.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
 import locale
 import os
 
@@ -7,7 +5,7 @@ import pytest
 
 import pip._internal.utils.compat as pip_compat
 from pip._internal.utils.compat import (
-    console_to_str, expanduser, get_path_uid, native_str, str_to_display,
+    console_to_str, expanduser, get_path_uid, native_str,
 )
 
 
@@ -47,58 +45,6 @@ def test_get_path_uid_symlink_without_NOFOLLOW(tmpdir, monkeypatch):
         get_path_uid(fs)
 
 
-@pytest.mark.parametrize('data, expected', [
-    ('abc', u'abc'),
-    # Test text (unicode in Python 2) input.
-    (u'abc', u'abc'),
-    # Test text input with non-ascii characters.
-    (u'déf', u'déf'),
-])
-def test_str_to_display(data, expected):
-    actual = str_to_display(data)
-    assert actual == expected, (
-        # Show the encoding for easier troubleshooting.
-        'encoding: {!r}'.format(locale.getpreferredencoding())
-    )
-
-
-@pytest.mark.parametrize('data, encoding, expected', [
-    # Test str input with non-ascii characters.
-    ('déf', 'utf-8', u'déf'),
-    # Test bytes input with non-ascii characters:
-    (u'déf'.encode('utf-8'), 'utf-8', u'déf'),
-    # Test a Windows encoding.
-    (u'déf'.encode('cp1252'), 'cp1252', u'déf'),
-    # Test a Windows encoding with incompatibly encoded text.
-    (u'déf'.encode('utf-8'), 'cp1252', u'dÃ©f'),
-])
-def test_str_to_display__encoding(monkeypatch, data, encoding, expected):
-    monkeypatch.setattr(locale, 'getpreferredencoding', lambda: encoding)
-    actual = str_to_display(data)
-    assert actual == expected, (
-        # Show the encoding for easier troubleshooting.
-        'encoding: {!r}'.format(locale.getpreferredencoding())
-    )
-
-
-def test_str_to_display__decode_error(monkeypatch, caplog):
-    monkeypatch.setattr(locale, 'getpreferredencoding', lambda: 'utf-8')
-    # Encode with an incompatible encoding.
-    data = u'ab'.encode('utf-16')
-    actual = str_to_display(data)
-
-    assert actual == u'\\xff\\xfea\x00b\x00', (
-        # Show the encoding for easier troubleshooting.
-        'encoding: {!r}'.format(locale.getpreferredencoding())
-    )
-    assert len(caplog.records) == 1
-    record = caplog.records[0]
-    assert record.levelname == 'WARNING'
-    assert record.message == (
-        'Bytes object does not appear to be encoded as utf-8'
-    )
-
-
 def test_console_to_str(monkeypatch):
     some_bytes = b"a\xE9\xC3\xE9b"
     encodings = ('ascii', 'utf-8', 'iso-8859-1', 'iso-8859-5',
diff --git a/tests/unit/test_finder.py b/tests/unit/test_finder.py
index 59c7b5ac7..7a79fb51f 100644
--- a/tests/unit/test_finder.py
+++ b/tests/unit/test_finder.py
@@ -3,7 +3,7 @@ import sys
 
 import pytest
 from mock import Mock, patch
-from pkg_resources import parse_version
+from pkg_resources import Distribution, parse_version
 
 import pip._internal.pep425tags
 import pip._internal.wheel
@@ -11,7 +11,7 @@ from pip._internal.exceptions import (
     BestVersionAlreadyInstalled, DistributionNotFound,
 )
 from pip._internal.index import (
-    CandidateEvaluator, InstallationCandidate, Link, LinkEvaluator,
+    CandidateEvaluator, InstallationCandidate, Link, Search,
 )
 from pip._internal.models.target_python import TargetPython
 from pip._internal.req.constructors import install_req_from_line
@@ -216,7 +216,12 @@ class TestWheel:
             ('pyT', 'TEST', 'any'),
             ('pyT', 'none', 'any'),
         ]
-        evaluator = CandidateEvaluator(supported_tags=valid_tags)
+        target_python = TargetPython()
+        target_python._valid_tags = valid_tags
+        evaluator = CandidateEvaluator(
+            allow_yanked=True,
+            target_python=target_python,
+        )
         sort_key = evaluator._sort_key
         results = sorted(links, key=sort_key, reverse=True)
         results2 = sorted(reversed(links), key=sort_key, reverse=True)
@@ -414,17 +419,17 @@ def test_finder_installs_pre_releases_with_version_spec():
         assert link.url == "https://foo/bar-2.0b1.tar.gz"
 
 
-class TestLinkEvaluator(object):
+class TestCandidateEvaluator(object):
 
-    def make_test_link_evaluator(self, formats):
-        target_python = TargetPython()
-        return LinkEvaluator(
-            project_name='pytest',
-            canonical_name='pytest',
-            formats=formats,
-            target_python=target_python,
-            allow_yanked=True,
-        )
+    # patch this for travis which has distribute in its base env for now
+    @patch(
+        'pip._internal.wheel.pkg_resources.get_distribution',
+        lambda x: Distribution(project_name='setuptools', version='0.9')
+    )
+    def setup(self):
+        self.search_name = 'pytest'
+        self.canonical_name = 'pytest'
+        self.evaluator = CandidateEvaluator(allow_yanked=True)
 
     @pytest.mark.parametrize('url, expected_version', [
         ('http:/yo/pytest-1.0.tar.gz', '1.0'),
@@ -433,8 +438,12 @@ class TestLinkEvaluator(object):
     def test_evaluate_link__match(self, url, expected_version):
         """Test that 'pytest' archives match for 'pytest'"""
         link = Link(url)
-        evaluator = self.make_test_link_evaluator(formats=['source', 'binary'])
-        actual = evaluator.evaluate_link(link)
+        search = Search(
+            supplied=self.search_name,
+            canonical=self.canonical_name,
+            formats=['source', 'binary'],
+        )
+        actual = self.evaluator.evaluate_link(link, search)
         assert actual == (True, expected_version)
 
     @pytest.mark.parametrize('url, expected_msg', [
@@ -448,8 +457,12 @@ class TestLinkEvaluator(object):
     def test_evaluate_link__substring_fails(self, url, expected_msg):
         """Test that 'pytest<something> archives won't match for 'pytest'."""
         link = Link(url)
-        evaluator = self.make_test_link_evaluator(formats=['source', 'binary'])
-        actual = evaluator.evaluate_link(link)
+        search = Search(
+            supplied=self.search_name,
+            canonical=self.canonical_name,
+            formats=['source', 'binary'],
+        )
+        actual = self.evaluator.evaluate_link(link, search)
         assert actual == (False, expected_msg)
 
 
diff --git a/tests/unit/test_index.py b/tests/unit/test_index.py
index 2d32867bd..b653660d2 100644
--- a/tests/unit/test_index.py
+++ b/tests/unit/test_index.py
@@ -4,14 +4,12 @@ import os.path
 import pytest
 from mock import Mock
 from pip._vendor import html5lib, requests
-from pip._vendor.packaging.specifiers import SpecifierSet
 
 from pip._internal.download import PipSession
 from pip._internal.index import (
-    CandidateEvaluator, FormatControl, HTMLPage, Link, LinkEvaluator,
-    PackageFinder, _check_link_requires_python, _clean_link,
-    _determine_base_url, _extract_version_from_fragment,
-    _find_name_version_sep, _get_html_page,
+    CandidateEvaluator, HTMLPage, Link, PackageFinder, Search,
+    _check_link_requires_python, _clean_link, _determine_base_url,
+    _extract_version_from_fragment, _find_name_version_sep, _get_html_page,
 )
 from pip._internal.models.candidate import InstallationCandidate
 from pip._internal.models.search_scope import SearchScope
@@ -86,7 +84,32 @@ def test_check_link_requires_python__invalid_requires(caplog):
     check_caplog(caplog, 'DEBUG', expected_message)
 
 
-class TestLinkEvaluator:
+class TestCandidateEvaluator:
+
+    def test_init__target_python(self):
+        """
+        Test the target_python argument.
+        """
+        target_python = TargetPython(py_version_info=(3, 7, 3))
+        evaluator = CandidateEvaluator(
+            allow_yanked=True,
+            target_python=target_python,
+        )
+        # The target_python attribute should be set as is.
+        assert evaluator._target_python is target_python
+
+    def test_init__target_python_none(self):
+        """
+        Test passing None for the target_python argument.
+        """
+        evaluator = CandidateEvaluator(
+            allow_yanked=True,
+            target_python=None,
+        )
+        # Spot-check the default TargetPython object.
+        actual_target_python = evaluator._target_python
+        assert actual_target_python._given_py_version_info is None
+        assert actual_target_python.py_version_info == CURRENT_PY_VERSION_INFO
 
     @pytest.mark.parametrize(
         'py_version_info,ignore_requires_python,expected', [
@@ -101,19 +124,19 @@ class TestLinkEvaluator:
         self, py_version_info, ignore_requires_python, expected,
     ):
         target_python = TargetPython(py_version_info=py_version_info)
-        evaluator = LinkEvaluator(
-            project_name='twine',
-            canonical_name='twine',
-            formats={'source'},
-            target_python=target_python,
+        evaluator = CandidateEvaluator(
             allow_yanked=True,
+            target_python=target_python,
             ignore_requires_python=ignore_requires_python,
         )
         link = Link(
             'https://example.com/#egg=twine-1.12',
             requires_python='== 3.6.5',
         )
-        actual = evaluator.evaluate_link(link)
+        search = Search(
+            supplied='twine', canonical='twine', formats=['source'],
+        )
+        actual = evaluator.evaluate_link(link, search=search)
         assert actual == expected
 
     @pytest.mark.parametrize('yanked_reason, allow_yanked, expected', [
@@ -132,19 +155,15 @@ class TestLinkEvaluator:
     def test_evaluate_link__allow_yanked(
         self, yanked_reason, allow_yanked, expected,
     ):
-        target_python = TargetPython(py_version_info=(3, 6, 4))
-        evaluator = LinkEvaluator(
-            project_name='twine',
-            canonical_name='twine',
-            formats={'source'},
-            target_python=target_python,
-            allow_yanked=allow_yanked,
-        )
+        evaluator = CandidateEvaluator(allow_yanked=allow_yanked)
         link = Link(
             'https://example.com/#egg=twine-1.12',
             yanked_reason=yanked_reason,
         )
-        actual = evaluator.evaluate_link(link)
+        search = Search(
+            supplied='twine', canonical='twine', formats=['source'],
+        )
+        actual = evaluator.evaluate_link(link, search=search)
         assert actual == expected
 
     def test_evaluate_link__incompatible_wheel(self):
@@ -154,50 +173,20 @@ class TestLinkEvaluator:
         target_python = TargetPython(py_version_info=(3, 6, 4))
         # Set the valid tags to an empty list to make sure nothing matches.
         target_python._valid_tags = []
-        evaluator = LinkEvaluator(
-            project_name='sample',
-            canonical_name='sample',
-            formats={'binary'},
-            target_python=target_python,
+        evaluator = CandidateEvaluator(
             allow_yanked=True,
+            target_python=target_python,
         )
         link = Link('https://example.com/sample-1.0-py2.py3-none-any.whl')
-        actual = evaluator.evaluate_link(link)
+        search = Search(
+            supplied='sample', canonical='sample', formats=['binary'],
+        )
+        actual = evaluator.evaluate_link(link, search=search)
         expected = (
             False, "none of the wheel's tags match: py2-none-any, py3-none-any"
         )
         assert actual == expected
 
-
-class TestCandidateEvaluator:
-
-    def make_mock_candidate(self, version, yanked_reason=None):
-        url = 'https://example.com/pkg-{}.tar.gz'.format(version)
-        link = Link(url, yanked_reason=yanked_reason)
-        candidate = InstallationCandidate('mypackage', version, link)
-
-        return candidate
-
-    def test_make_found_candidates(self):
-        specifier = SpecifierSet('<= 1.11')
-        versions = ['1.10', '1.11', '1.12']
-        candidates = [
-            self.make_mock_candidate(version) for version in versions
-        ]
-        evaluator = CandidateEvaluator()
-        found_candidates = evaluator.make_found_candidates(
-            candidates, specifier=specifier,
-        )
-
-        assert found_candidates._candidates == candidates
-        assert found_candidates._evaluator is evaluator
-        expected_applicable = candidates[:2]
-        assert [str(c.version) for c in expected_applicable] == [
-            '1.10',
-            '1.11',
-        ]
-        assert found_candidates._applicable_candidates == expected_applicable
-
     @pytest.mark.parametrize('yanked_reason, expected', [
         # Test a non-yanked file.
         (None, 0),
@@ -212,17 +201,24 @@ class TestCandidateEvaluator:
         link = Link(url, yanked_reason=yanked_reason)
         candidate = InstallationCandidate('mypackage', '1.0', link)
 
-        evaluator = CandidateEvaluator()
+        evaluator = CandidateEvaluator(allow_yanked=True)
         sort_value = evaluator._sort_key(candidate)
         # Yanked / non-yanked is reflected in the first element of the tuple.
         actual = sort_value[0]
         assert actual == expected
 
+    def make_mock_candidate(self, version, yanked_reason=None):
+        url = 'https://example.com/pkg-{}.tar.gz'.format(version)
+        link = Link(url, yanked_reason=yanked_reason)
+        candidate = InstallationCandidate('mypackage', version, link)
+
+        return candidate
+
     def test_get_best_candidate__no_candidates(self):
         """
         Test passing an empty list.
         """
-        evaluator = CandidateEvaluator()
+        evaluator = CandidateEvaluator(allow_yanked=True)
         actual = evaluator.get_best_candidate([])
         assert actual is None
 
@@ -237,7 +233,7 @@ class TestCandidateEvaluator:
             self.make_mock_candidate('2.0', yanked_reason='bad metadata #2'),
         ]
         expected_best = candidates[1]
-        evaluator = CandidateEvaluator()
+        evaluator = CandidateEvaluator(allow_yanked=True)
         actual = evaluator.get_best_candidate(candidates)
         assert actual is expected_best
         assert str(actual.version) == '3.0'
@@ -268,7 +264,7 @@ class TestCandidateEvaluator:
         candidates = [
             self.make_mock_candidate('1.0', yanked_reason=yanked_reason),
         ]
-        evaluator = CandidateEvaluator()
+        evaluator = CandidateEvaluator(allow_yanked=True)
         actual = evaluator.get_best_candidate(candidates)
         assert str(actual.version) == '1.0'
 
@@ -295,7 +291,7 @@ class TestCandidateEvaluator:
             self.make_mock_candidate('1.0'),
         ]
         expected_best = candidates[1]
-        evaluator = CandidateEvaluator()
+        evaluator = CandidateEvaluator(allow_yanked=True)
         actual = evaluator.get_best_candidate(candidates)
         assert actual is expected_best
         assert str(actual.version) == '2.0'
@@ -306,115 +302,42 @@ class TestCandidateEvaluator:
 
 class TestPackageFinder:
 
-    @pytest.mark.parametrize('allow_all_prereleases, prefer_binary', [
-        (False, False),
-        (False, True),
-        (True, False),
-        (True, True),
-    ])
-    def test_create__candidate_evaluator(
-        self, allow_all_prereleases, prefer_binary,
-    ):
-        """
-        Test that the candidate_evaluator attribute is set correctly.
-        """
-        selection_prefs = SelectionPreferences(
-            allow_yanked=True,
-            allow_all_prereleases=allow_all_prereleases,
-            prefer_binary=prefer_binary,
-        )
-        target_python = TargetPython(py_version_info=(3, 7, 3))
-        target_python._valid_tags = ['tag1', 'tag2']
-        finder = PackageFinder.create(
-            search_scope=SearchScope([], []),
-            selection_prefs=selection_prefs,
-            session=PipSession(),
-            target_python=target_python,
-        )
-        evaluator = finder.candidate_evaluator
-        assert evaluator.allow_all_prereleases == allow_all_prereleases
-        assert evaluator._prefer_binary == prefer_binary
-        assert evaluator._supported_tags == ['tag1', 'tag2']
-
-    def test_create__target_python(self):
-        """
-        Test that the _target_python attribute is set correctly.
-        """
-        target_python = TargetPython(py_version_info=(3, 7, 3))
-        finder = PackageFinder.create(
-            search_scope=SearchScope([], []),
-            selection_prefs=SelectionPreferences(allow_yanked=True),
-            session=PipSession(),
-            target_python=target_python,
-        )
-        actual_target_python = finder._target_python
-        # The target_python attribute should be set as is.
-        assert actual_target_python is target_python
-        # Check that the attributes weren't reset.
-        assert actual_target_python.py_version_info == (3, 7, 3)
-
-    def test_create__target_python_none(self):
-        """
-        Test passing target_python=None.
-        """
-        finder = PackageFinder.create(
-            search_scope=SearchScope([], []),
-            selection_prefs=SelectionPreferences(allow_yanked=True),
-            session=PipSession(),
-            target_python=None,
-        )
-        # Spot-check the default TargetPython object.
-        actual_target_python = finder._target_python
-        assert actual_target_python._given_py_version_info is None
-        assert actual_target_python.py_version_info == CURRENT_PY_VERSION_INFO
-
     @pytest.mark.parametrize('allow_yanked', [False, True])
     def test_create__allow_yanked(self, allow_yanked):
         """
-        Test that the _allow_yanked attribute is set correctly.
-        """
-        selection_prefs = SelectionPreferences(allow_yanked=allow_yanked)
-        finder = PackageFinder.create(
-            search_scope=SearchScope([], []),
-            selection_prefs=selection_prefs,
-            session=PipSession(),
-        )
-        assert finder._allow_yanked == allow_yanked
-
-    @pytest.mark.parametrize('ignore_requires_python', [False, True])
-    def test_create__ignore_requires_python(self, ignore_requires_python):
-        """
-        Test that the _ignore_requires_python attribute is set correctly.
+        Test that allow_yanked is passed to CandidateEvaluator.
         """
+        search_scope = SearchScope([], [])
         selection_prefs = SelectionPreferences(
-            allow_yanked=True,
-            ignore_requires_python=ignore_requires_python,
+            allow_yanked=allow_yanked,
         )
         finder = PackageFinder.create(
-            search_scope=SearchScope([], []),
+            search_scope=search_scope,
             selection_prefs=selection_prefs,
-            session=PipSession(),
+            session=object(),
         )
-        assert finder._ignore_requires_python == ignore_requires_python
+        evaluator = finder.candidate_evaluator
+        assert evaluator._allow_yanked == allow_yanked
 
-    def test_create__format_control(self):
+    def test_create__target_python(self):
         """
-        Test that the format_control attribute is set correctly.
+        Test that target_python is passed to CandidateEvaluator as is.
         """
-        format_control = FormatControl(set(), {':all:'})
+        search_scope = SearchScope([], [])
         selection_prefs = SelectionPreferences(
             allow_yanked=True,
-            format_control=format_control,
         )
+        target_python = TargetPython(py_version_info=(3, 7, 3))
         finder = PackageFinder.create(
-            search_scope=SearchScope([], []),
+            search_scope=search_scope,
             selection_prefs=selection_prefs,
-            session=PipSession(),
+            session=object(),
+            target_python=target_python,
         )
-        actual_format_control = finder.format_control
-        assert actual_format_control is format_control
-        # Check that the attributes weren't reset.
-        assert actual_format_control.only_binary == {':all:'}
+        evaluator = finder.candidate_evaluator
+        actual_target_python = evaluator._target_python
+        assert actual_target_python is target_python
+        assert actual_target_python.py_version_info == (3, 7, 3)
 
     def test_add_trusted_host(self):
         # Leave a gap to test how the ordering is affected.
@@ -506,52 +429,6 @@ class TestPackageFinder:
         # Spot-check that SECURE_ORIGINS is included.
         assert actual[0] == ('https', '*', '*')
 
-    @pytest.mark.parametrize(
-        'allow_yanked, ignore_requires_python, only_binary, expected_formats',
-        [
-            (False, False, {}, frozenset({'binary', 'source'})),
-            # Test allow_yanked=True.
-            (True, False, {}, frozenset({'binary', 'source'})),
-            # Test ignore_requires_python=True.
-            (False, True, {}, frozenset({'binary', 'source'})),
-            # Test a non-trivial only_binary.
-            (False, False, {'twine'}, frozenset({'binary'})),
-        ]
-    )
-    def test_make_link_evaluator(
-        self, allow_yanked, ignore_requires_python, only_binary,
-        expected_formats,
-    ):
-        # Create a test TargetPython that we can check for.
-        target_python = TargetPython(py_version_info=(3, 7))
-        format_control = FormatControl(set(), only_binary)
-        finder = PackageFinder(
-            candidate_evaluator=CandidateEvaluator(),
-            search_scope=SearchScope([], []),
-            session=PipSession(),
-            target_python=target_python,
-            allow_yanked=allow_yanked,
-            format_control=format_control,
-            ignore_requires_python=ignore_requires_python,
-        )
-
-        # Pass a project_name that will be different from canonical_name.
-        link_evaluator = finder.make_link_evaluator('Twine')
-
-        assert link_evaluator.project_name == 'Twine'
-        assert link_evaluator._canonical_name == 'twine'
-        assert link_evaluator._allow_yanked == allow_yanked
-        assert link_evaluator._ignore_requires_python == ignore_requires_python
-        assert link_evaluator._formats == expected_formats
-
-        # Test the _target_python attribute.
-        actual_target_python = link_evaluator._target_python
-        # The target_python attribute should be set as is.
-        assert actual_target_python is target_python
-        # For good measure, check that the attributes weren't reset.
-        assert actual_target_python._given_py_version_info == (3, 7)
-        assert actual_target_python.py_version_info == (3, 7, 0)
-
 
 def test_sort_locations_file_expand_dir(data):
     """
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index b7be0d811..fdc1dd03c 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -6,7 +6,6 @@ util tests
 """
 import codecs
 import itertools
-import locale
 import os
 import shutil
 import stat
@@ -768,38 +767,10 @@ def test_make_subprocess_output_error():
     assert actual == expected, 'actual: {}'.format(actual)
 
 
-def test_make_subprocess_output_error__non_ascii_command_arg(monkeypatch):
-    """
-    Test a command argument with a non-ascii character.
-    """
-    cmd_args = ['foo', 'déf']
-    if sys.version_info[0] == 2:
-        # Check in Python 2 that the str (bytes object) with the non-ascii
-        # character has the encoding we expect. (This comes from the source
-        # code encoding at the top of the file.)
-        assert cmd_args[1].decode('utf-8') == u'déf'
-
-    # We need to monkeypatch so the encoding will be correct on Windows.
-    monkeypatch.setattr(locale, 'getpreferredencoding', lambda: 'utf-8')
-    actual = make_subprocess_output_error(
-        cmd_args=cmd_args,
-        cwd='/path/to/cwd',
-        lines=[],
-        exit_status=1,
-    )
-    expected = dedent(u"""\
-    Command errored out with exit status 1:
-     command: foo 'déf'
-         cwd: /path/to/cwd
-    Complete output (0 lines):
-    ----------------------------------------""")
-    assert actual == expected, u'actual: {}'.format(actual)
-
-
 # This test is mainly important for checking unicode in Python 2.
-def test_make_subprocess_output_error__non_ascii_line():
+def test_make_subprocess_output_error__unicode():
     """
-    Test a line with a non-ascii character.
+    Test a line with non-ascii unicode characters.
     """
     lines = [u'curly-quote: \u2018\n']
     actual = make_subprocess_output_error(
diff --git a/tests/unit/test_vcs.py b/tests/unit/test_vcs.py
index 66754c667..03d2229d8 100644
--- a/tests/unit/test_vcs.py
+++ b/tests/unit/test_vcs.py
@@ -98,17 +98,14 @@ def test_rev_options_make_new():
     assert new_options.vc_class is Git
 
 
-@pytest.mark.parametrize('sha, expected', [
-    ((40 * 'a'), True),
-    ((40 * 'A'), True),
+def test_looks_like_hash():
+    assert looks_like_hash(40 * 'a')
+    assert looks_like_hash(40 * 'A')
     # Test a string containing all valid characters.
-    ((18 * 'a' + '0123456789abcdefABCDEF'), True),
-    ((40 * 'g'), False),
-    ((39 * 'a'), False),
-    ((41 * 'a'), False)
-])
-def test_looks_like_hash(sha, expected):
-    assert looks_like_hash(sha) == expected
+    assert looks_like_hash(18 * 'a' + '0123456789abcdefABCDEF')
+    assert not looks_like_hash(40 * 'g')
+    assert not looks_like_hash(39 * 'a')
+    assert not looks_like_hash(41 * 'a')
 
 
 @pytest.mark.parametrize('vcs_cls, remote_url, expected', [

pip install -e .
parse_log_pytest
pytest
pytest pytest-cov pytest-rerunfailures pytest-timeout pytest-xdist freezegun mock pretend pyyaml setuptools>=39.2.0 scripttest virtualenv wheel
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
coverage==7.8.0
distlib==0.3.9
exceptiongroup==1.2.0
execnet==2.1.1
filelock==3.18.0
freezegun==1.5.1
iniconfig==1.1.1
mock==5.2.0
packaging==24.2
platformdirs==4.3.7
pluggy==1.5.0
pretend==1.0.9
pytest==8.3.4
pytest-cov==6.0.0
pytest-rerunfailures==15.0
pytest-timeout==2.3.1
pytest-xdist==3.6.1
python-dateutil==2.9.0.post0
PyYAML==6.0.2
scripttest==2.0
six==1.17.0
tomli==2.0.1
virtualenv==20.29.3

name: pip
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py39h06a4308_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coverage==7.8.0
      - distlib==0.3.9
      - execnet==2.1.1
      - filelock==3.18.0
      - freezegun==1.5.1
      - mock==5.2.0
      - pip==19.2.dev0
      - platformdirs==4.3.7
      - pretend==1.0.9
      - pytest-cov==6.0.0
      - pytest-rerunfailures==15.0
      - pytest-timeout==2.3.1
      - pytest-xdist==3.6.1
      - python-dateutil==2.9.0.post0
      - pyyaml==6.0.2
      - scripttest==2.0
      - six==1.17.0
      - virtualenv==20.29.3
prefix: /opt/conda/envs/pip

tests/unit/test_compat.py::test_get_path_uid tests/unit/test_compat.py::test_get_path_uid_without_NOFOLLOW tests/unit/test_compat.py::test_get_path_uid_symlink tests/unit/test_compat.py::test_get_path_uid_symlink_without_NOFOLLOW tests/unit/test_compat.py::test_console_to_str tests/unit/test_compat.py::test_console_to_str_warning tests/unit/test_compat.py::test_to_native_str_type tests/unit/test_compat.py::test_expanduser[/Users/test-~-/Users/test] tests/unit/test_compat.py::test_expanduser[/Users/test-~/.cache-/Users/test/.cache] tests/unit/test_compat.py::test_expanduser[/-~-/] tests/unit/test_compat.py::test_expanduser[/-~/.cache-/.cache] tests/unit/test_finder.py::test_no_mpkg tests/unit/test_finder.py::test_no_partial_name_match tests/unit/test_finder.py::test_tilde tests/unit/test_finder.py::test_duplicates_sort_ok tests/unit/test_finder.py::test_finder_detects_latest_find_links tests/unit/test_finder.py::test_incorrect_case_file_index tests/unit/test_finder.py::test_finder_detects_latest_already_satisfied_find_links tests/unit/test_finder.py::test_finder_detects_latest_already_satisfied_pypi_links tests/unit/test_finder.py::TestWheel::test_skip_invalid_wheel_link tests/unit/test_finder.py::TestWheel::test_not_find_wheel_not_supported tests/unit/test_finder.py::TestWheel::test_find_wheel_supported tests/unit/test_finder.py::TestWheel::test_wheel_over_sdist_priority tests/unit/test_finder.py::TestWheel::test_existing_over_wheel_priority tests/unit/test_finder.py::TestWheel::test_link_sorting tests/unit/test_finder.py::TestWheel::test_link_sorting_wheels_with_build_tags tests/unit/test_finder.py::test_finder_priority_file_over_page tests/unit/test_finder.py::test_finder_priority_nonegg_over_eggfragments tests/unit/test_finder.py::test_finder_only_installs_stable_releases tests/unit/test_finder.py::test_finder_only_installs_data_require tests/unit/test_finder.py::test_finder_installs_pre_releases tests/unit/test_finder.py::test_finder_installs_dev_releases tests/unit/test_finder.py::test_finder_installs_pre_releases_with_version_spec tests/unit/test_finder.py::test_find_all_candidates_nothing tests/unit/test_finder.py::test_find_all_candidates_find_links tests/unit/test_finder.py::test_find_all_candidates_index tests/unit/test_finder.py::test_find_all_candidates_find_links_and_index tests/unit/test_index.py::test_check_link_requires_python[== tests/unit/test_index.py::test_check_link_requires_python[invalid-True] tests/unit/test_index.py::test_check_link_requires_python__incompatible_python[None-expected0] tests/unit/test_index.py::test_check_link_requires_python__incompatible_python[True-expected1] tests/unit/test_index.py::test_check_link_requires_python__invalid_requires tests/unit/test_index.py::TestCandidateEvaluator::test_init__target_python tests/unit/test_index.py::TestCandidateEvaluator::test_init__target_python_none tests/unit/test_index.py::TestCandidateEvaluator::test_evaluate_link[py_version_info0-None-expected0] tests/unit/test_index.py::TestCandidateEvaluator::test_evaluate_link[py_version_info1-None-expected1] tests/unit/test_index.py::TestCandidateEvaluator::test_evaluate_link[py_version_info2-True-expected2] tests/unit/test_index.py::TestCandidateEvaluator::test_evaluate_link__allow_yanked[None-True-expected0] tests/unit/test_index.py::TestCandidateEvaluator::test_evaluate_link__allow_yanked[None-False-expected1] tests/unit/test_index.py::TestCandidateEvaluator::test_evaluate_link__allow_yanked[-True-expected2] tests/unit/test_index.py::TestCandidateEvaluator::test_evaluate_link__allow_yanked[-False-expected3] tests/unit/test_index.py::TestCandidateEvaluator::test_evaluate_link__allow_yanked[bad tests/unit/test_index.py::TestCandidateEvaluator::test_evaluate_link__allow_yanked[curly tests/unit/test_index.py::TestCandidateEvaluator::test_evaluate_link__incompatible_wheel tests/unit/test_index.py::TestCandidateEvaluator::test_sort_key__is_yanked[None-0] tests/unit/test_index.py::TestCandidateEvaluator::test_sort_key__is_yanked[bad tests/unit/test_index.py::TestCandidateEvaluator::test_get_best_candidate__no_candidates tests/unit/test_index.py::TestCandidateEvaluator::test_get_best_candidate__all_yanked tests/unit/test_index.py::TestCandidateEvaluator::test_get_best_candidate__yanked_reason[-<none tests/unit/test_index.py::TestCandidateEvaluator::test_get_best_candidate__yanked_reason[curly tests/unit/test_index.py::TestCandidateEvaluator::test_get_best_candidate__best_yanked_but_not_all tests/unit/test_index.py::TestPackageFinder::test_create__allow_yanked[False] tests/unit/test_index.py::TestPackageFinder::test_create__allow_yanked[True] tests/unit/test_index.py::TestPackageFinder::test_create__target_python tests/unit/test_index.py::TestPackageFinder::test_add_trusted_host tests/unit/test_index.py::TestPackageFinder::test_add_trusted_host__logging tests/unit/test_index.py::TestPackageFinder::test_iter_secure_origins tests/unit/test_index.py::TestPackageFinder::test_iter_secure_origins__none_trusted_hosts tests/unit/test_index.py::test_sort_locations_file_expand_dir tests/unit/test_index.py::test_sort_locations_file_not_find_link tests/unit/test_index.py::test_sort_locations_non_existing_path tests/unit/test_index.py::test_determine_base_url[<html></html>-https://example.com/-https://example.com/] tests/unit/test_index.py::test_determine_base_url[<html><head><base tests/unit/test_index.py::test_determine_base_url[<html><head><base><base tests/unit/test_index.py::test_secure_origin[http://pypi.org/something-trusted0-True] tests/unit/test_index.py::test_secure_origin[https://pypi.org/something-trusted1-False] tests/unit/test_index.py::test_secure_origin[git+http://pypi.org/something-trusted2-True] tests/unit/test_index.py::test_secure_origin[git+https://pypi.org/something-trusted3-False] tests/unit/test_index.py::test_secure_origin[git+ssh://git@pypi.org/something-trusted4-False] tests/unit/test_index.py::test_secure_origin[http://localhost-trusted5-False] tests/unit/test_index.py::test_secure_origin[http://127.0.0.1-trusted6-False] tests/unit/test_index.py::test_secure_origin[http://example.com/something/-trusted7-True] tests/unit/test_index.py::test_secure_origin[http://example.com/something/-trusted8-False] tests/unit/test_index.py::test_secure_origin[http://eXample.com/something/-trusted9-False] tests/unit/test_index.py::test_find_name_version_sep[pip-18.0-pip-3] tests/unit/test_index.py::test_find_name_version_sep[zope-interface-4.5.0-zope-interface-14] tests/unit/test_index.py::test_find_name_version_sep[Jinja2-2.10-jinja2-6] tests/unit/test_index.py::test_find_name_version_sep[zope.interface-4.5.0-zope-interface-14] tests/unit/test_index.py::test_find_name_version_sep[zope_interface-4.5.0-zope-interface-14] tests/unit/test_index.py::test_find_name_version_sep[foo-2-2-foo-3] tests/unit/test_index.py::test_find_name_version_sep[foo-2-2-foo-2-5] tests/unit/test_index.py::test_find_name_version_sep[foo--bar-1.0-foo-bar-8] tests/unit/test_index.py::test_find_name_version_sep[foo-_bar-1.0-foo-bar-8] tests/unit/test_index.py::test_find_name_version_sep[zope.interface--4.5.0-zope-interface-14] tests/unit/test_index.py::test_find_name_version_sep[zope.interface---zope-interface-14] tests/unit/test_index.py::test_find_name_version_sep[zope.interface--zope-interface-14] tests/unit/test_index.py::test_find_name_version_sep_failure[zope.interface4.5.0-zope-interface] tests/unit/test_index.py::test_find_name_version_sep_failure[zope.interface.4.5.0-zope-interface] tests/unit/test_index.py::test_find_name_version_sep_failure[zope.interface.-4.5.0-zope-interface] tests/unit/test_index.py::test_find_name_version_sep_failure[zope.interface-zope-interface] tests/unit/test_index.py::test_extract_version_from_fragment[pip-18.0-pip-18.0] tests/unit/test_index.py::test_extract_version_from_fragment[zope-interface-4.5.0-zope-interface-4.5.0] tests/unit/test_index.py::test_extract_version_from_fragment[Jinja2-2.10-jinja2-2.10] tests/unit/test_index.py::test_extract_version_from_fragment[zope.interface-4.5.0-zope-interface-4.5.0] tests/unit/test_index.py::test_extract_version_from_fragment[zope_interface-4.5.0-zope-interface-4.5.0] tests/unit/test_index.py::test_extract_version_from_fragment[foo-2-2-foo-2-2_0] tests/unit/test_index.py::test_extract_version_from_fragment[foo-2-2-foo-2-2_1] tests/unit/test_index.py::test_extract_version_from_fragment[zope.interface--4.5.0-zope-interface--4.5.0] tests/unit/test_index.py::test_extract_version_from_fragment[zope.interface---zope-interface--] tests/unit/test_index.py::test_extract_version_from_fragment[foo--bar-1.0-foo-bar-1.0] tests/unit/test_index.py::test_extract_version_from_fragment[foo-_bar-1.0-foo-bar-1.0] tests/unit/test_index.py::test_extract_version_from_fragment[the-package-name-8.19-does-not-match-None] tests/unit/test_index.py::test_extract_version_from_fragment[zope.interface.-4.5.0-zope.interface-None] tests/unit/test_index.py::test_extract_version_from_fragment[zope.interface--zope-interface-None] tests/unit/test_index.py::test_extract_version_from_fragment[zope.interface4.5.0-zope-interface-None] tests/unit/test_index.py::test_extract_version_from_fragment[zope.interface.4.5.0-zope-interface-None] tests/unit/test_index.py::test_extract_version_from_fragment[zope.interface.-4.5.0-zope-interface-None] tests/unit/test_index.py::test_extract_version_from_fragment[zope.interface-zope-interface-None] tests/unit/test_index.py::test_request_http_error tests/unit/test_index.py::test_request_retries tests/unit/test_index.py::test_clean_link[https://localhost.localdomain:8181/path/with tests/unit/test_index.py::test_clean_link[https://localhost.localdomain:8181/path/with%20quoted%20space/-https://localhost.localdomain:8181/path/with%20quoted%20space/] tests/unit/test_index.py::test_clean_link[https://127.0.0.1:8181/path/with tests/unit/test_index.py::test_clean_link[https://[fd00:0:0:236::100]:8181/path/with tests/unit/test_index.py::test_clean_link[https://localhost.localdomain:8181/path/with/query?request=test-https://localhost.localdomain:8181/path/with/query?request=test] tests/unit/test_index.py::test_clean_link[https://localhost.localdomain:8181/path:/with:/colon-https://localhost.localdomain:8181/path%3A/with%3A/colon] tests/unit/test_index.py::test_clean_link[https://localhost.localdomain/T:/path/-https://localhost.localdomain/T%3A/path/] tests/unit/test_index.py::test_clean_link[git+ssh://example.com/path tests/unit/test_index.py::test_clean_link_non_windows[file:///T:/path/with tests/unit/test_index.py::TestHTMLPage::test_iter_links__yanked_reason[<a tests/unit/test_utils.py::Tests_get_installed_distributions::test_editables_only tests/unit/test_utils.py::Tests_get_installed_distributions::test_exclude_editables tests/unit/test_utils.py::Tests_get_installed_distributions::test_include_globals tests/unit/test_utils.py::Tests_get_installed_distributions::test_user_only tests/unit/test_utils.py::Tests_get_installed_distributions::test_gte_py27_excludes tests/unit/test_utils.py::Tests_get_installed_distributions::test_freeze_excludes tests/unit/test_utils.py::test_rmtree_retries tests/unit/test_utils.py::test_rmtree_retries_for_3sec tests/unit/test_utils.py::Test_normalize_path::test_resolve_symlinks tests/unit/test_utils.py::TestHashes::test_success tests/unit/test_utils.py::TestHashes::test_failure tests/unit/test_utils.py::TestHashes::test_missing_hashes tests/unit/test_utils.py::TestHashes::test_unknown_hash tests/unit/test_utils.py::TestHashes::test_non_zero tests/unit/test_utils.py::TestEncoding::test_auto_decode_utf_16_le tests/unit/test_utils.py::TestEncoding::test_auto_decode_utf_16_be tests/unit/test_utils.py::TestEncoding::test_auto_decode_no_bom tests/unit/test_utils.py::TestEncoding::test_auto_decode_pep263_headers tests/unit/test_utils.py::TestEncoding::test_auto_decode_no_preferred_encoding tests/unit/test_utils.py::TestEncoding::test_all_encodings_are_valid[utf-8] tests/unit/test_utils.py::TestEncoding::test_all_encodings_are_valid[utf-16] tests/unit/test_utils.py::TestEncoding::test_all_encodings_are_valid[utf-16-be] tests/unit/test_utils.py::TestEncoding::test_all_encodings_are_valid[utf-16-le] tests/unit/test_utils.py::TestEncoding::test_all_encodings_are_valid[utf-32] tests/unit/test_utils.py::TestEncoding::test_all_encodings_are_valid[utf-32-be] tests/unit/test_utils.py::TestEncoding::test_all_encodings_are_valid[utf-32-le] tests/unit/test_utils.py::TestTempDirectory::test_symlinked_path tests/unit/test_utils.py::TestTempDirectory::test_deletes_readonly_files tests/unit/test_utils.py::TestTempDirectory::test_create_and_cleanup_work tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_names[ABC] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_names[ABC.dist-info] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_names[_+-] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_names[_package] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_names[A......B] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_names[AB] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_names[A] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_names[2] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_exists[A] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_exists[ABC] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_exists[ABC.dist-info] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_exists[_+-] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_exists[_package] tests/unit/test_utils.py::TestTempDirectory::test_adjacent_directory_permission_error tests/unit/test_utils.py::TestGlibc::test_manylinux_check_glibc_version tests/unit/test_utils.py::test_normalize_version_info[None-None] tests/unit/test_utils.py::test_normalize_version_info[version_info1-expected1] tests/unit/test_utils.py::test_normalize_version_info[version_info2-expected2] tests/unit/test_utils.py::test_normalize_version_info[version_info3-expected3] tests/unit/test_utils.py::test_normalize_version_info[version_info4-expected4] tests/unit/test_utils.py::test_normalize_version_info[version_info5-expected5] tests/unit/test_utils.py::TestGetProg::test_get_prog[/usr/bin/pip--pip] tests/unit/test_utils.py::TestGetProg::test_get_prog[-c-/usr/bin/python-/usr/bin/python tests/unit/test_utils.py::TestGetProg::test_get_prog[__main__.py-/usr/bin/python-/usr/bin/python tests/unit/test_utils.py::TestGetProg::test_get_prog[/usr/bin/pip3--pip3] tests/unit/test_utils.py::test_format_command_args[args0-pip tests/unit/test_utils.py::test_format_command_args[args1-foo tests/unit/test_utils.py::test_make_subprocess_output_error tests/unit/test_utils.py::test_make_subprocess_output_error__unicode tests/unit/test_utils.py::TestCallSubprocess::test_debug_logging tests/unit/test_utils.py::TestCallSubprocess::test_info_logging tests/unit/test_utils.py::TestCallSubprocess::test_info_logging__subprocess_error tests/unit/test_utils.py::TestCallSubprocess::test_info_logging_with_show_stdout_true tests/unit/test_utils.py::TestCallSubprocess::test_spinner_finish[0-False-None-20-expected0] tests/unit/test_utils.py::TestCallSubprocess::test_spinner_finish[0-False-None-10-expected1] tests/unit/test_utils.py::TestCallSubprocess::test_spinner_finish[0-True-None-10-expected2] tests/unit/test_utils.py::TestCallSubprocess::test_spinner_finish[0-True-None-20-expected3] tests/unit/test_utils.py::TestCallSubprocess::test_spinner_finish[0-True-None-30-expected4] tests/unit/test_utils.py::TestCallSubprocess::test_spinner_finish[3-False-None-20-expected5] tests/unit/test_utils.py::TestCallSubprocess::test_spinner_finish[3-False-extra_ok_returncodes6-20-expected6] tests/unit/test_utils.py::TestCallSubprocess::test_closes_stdin tests/unit/test_utils.py::test_path_to_url_unix tests/unit/test_utils.py::test_split_auth_from_netloc[example.com-expected0] tests/unit/test_utils.py::test_split_auth_from_netloc[user@example.com-expected1] tests/unit/test_utils.py::test_split_auth_from_netloc[user:pass@example.com-expected2] tests/unit/test_utils.py::test_split_auth_from_netloc[user:@example.com-expected3] tests/unit/test_utils.py::test_split_auth_from_netloc[user:pass@word@example.com-expected4] tests/unit/test_utils.py::test_split_auth_from_netloc[user:pass:word@example.com-expected5] tests/unit/test_utils.py::test_split_auth_from_netloc[user%3Aname:%23%40%5E@example.com-expected6] tests/unit/test_utils.py::test_split_auth_netloc_from_url[http://example.com/path#anchor-expected0] tests/unit/test_utils.py::test_split_auth_netloc_from_url[http://user@example.com/path#anchor-expected1] tests/unit/test_utils.py::test_split_auth_netloc_from_url[http://user:pass@example.com/path#anchor-expected2] tests/unit/test_utils.py::test_split_auth_netloc_from_url[http://user:@example.com/path#anchor-expected3] tests/unit/test_utils.py::test_split_auth_netloc_from_url[http://user:pass@word@example.com/path#anchor-expected4] tests/unit/test_utils.py::test_split_auth_netloc_from_url[http://user:pass:word@example.com/path#anchor-expected5] tests/unit/test_utils.py::test_split_auth_netloc_from_url[http://user%3Aname:%23%40%5E@example.com/path#anchor-expected6] tests/unit/test_utils.py::test_redact_netloc[example.com-example.com] tests/unit/test_utils.py::test_redact_netloc[user@example.com-user@example.com] tests/unit/test_utils.py::test_redact_netloc[user:pass@example.com-user:****@example.com] tests/unit/test_utils.py::test_redact_netloc[user:@example.com-user:****@example.com] tests/unit/test_utils.py::test_redact_netloc[user:pass@word@example.com-user:****@example.com] tests/unit/test_utils.py::test_redact_netloc[user:pass:word@example.com-user:****@example.com] tests/unit/test_utils.py::test_redact_netloc[user%3Aname:%23%40%5E@example.com-user%3Aname:****@example.com] tests/unit/test_utils.py::test_remove_auth_from_url[https://user:pass@domain.tld/project/tags/v0.2-https://domain.tld/project/tags/v0.2] tests/unit/test_utils.py::test_remove_auth_from_url[https://domain.tld/project/tags/v0.2-https://domain.tld/project/tags/v0.2] tests/unit/test_utils.py::test_remove_auth_from_url[https://user:pass@domain.tld/svn/project/trunk@8181-https://domain.tld/svn/project/trunk@8181] tests/unit/test_utils.py::test_remove_auth_from_url[https://domain.tld/project/trunk@8181-https://domain.tld/project/trunk@8181] tests/unit/test_utils.py::test_remove_auth_from_url[git+https://pypi.org/something-git+https://pypi.org/something] tests/unit/test_utils.py::test_remove_auth_from_url[git+https://user:pass@pypi.org/something-git+https://pypi.org/something] tests/unit/test_utils.py::test_remove_auth_from_url[git+ssh://git@pypi.org/something-git+ssh://pypi.org/something] tests/unit/test_utils.py::test_redact_password_from_url[https://user@example.com/abc-https://user@example.com/abc] tests/unit/test_utils.py::test_redact_password_from_url[https://user:password@example.com-https://user:****@example.com] tests/unit/test_utils.py::test_redact_password_from_url[https://user:@example.com-https://user:****@example.com] tests/unit/test_utils.py::test_redact_password_from_url[https://example.com-https://example.com] tests/unit/test_utils.py::test_redact_password_from_url[https://user%3Aname:%23%40%5E@example.com-https://user%3Aname:****@example.com] tests/unit/test_utils.py::test_deprecated_message_contains_information[None-None-None] tests/unit/test_utils.py::test_deprecated_message_contains_information[None-None-a tests/unit/test_utils.py::test_deprecated_message_contains_information[None-2.0-None] tests/unit/test_utils.py::test_deprecated_message_contains_information[None-2.0-a tests/unit/test_utils.py::test_deprecated_message_contains_information[988-None-None] tests/unit/test_utils.py::test_deprecated_message_contains_information[988-None-a tests/unit/test_utils.py::test_deprecated_message_contains_information[988-2.0-None] tests/unit/test_utils.py::test_deprecated_message_contains_information[988-2.0-a tests/unit/test_utils.py::test_deprecated_raises_error_if_too_old[None-None] tests/unit/test_utils.py::test_deprecated_raises_error_if_too_old[None-a tests/unit/test_utils.py::test_deprecated_raises_error_if_too_old[988-None] tests/unit/test_utils.py::test_deprecated_raises_error_if_too_old[988-a tests/unit/test_utils.py::test_deprecated_message_reads_well tests/unit/test_vcs.py::test_make_vcs_requirement_url[args0-git+https://example.com/pkg@dev#egg=myproj] tests/unit/test_vcs.py::test_make_vcs_requirement_url[args1-git+https://example.com/pkg@dev#egg=myproj&subdirectory=sub/dir] tests/unit/test_vcs.py::test_make_vcs_requirement_url[args2-git+https://example.com/pkg@dev#egg=myproj] tests/unit/test_vcs.py::test_make_vcs_requirement_url[args3-git+https://example.com/pkg@dev#egg=zope_interface] tests/unit/test_vcs.py::test_rev_options_repr tests/unit/test_vcs.py::test_rev_options_to_args[Bazaar-expected10-expected20-kwargs0] tests/unit/test_vcs.py::test_rev_options_to_args[Git-expected11-expected21-kwargs1] tests/unit/test_vcs.py::test_rev_options_to_args[Mercurial-expected12-expected22-kwargs2] tests/unit/test_vcs.py::test_rev_options_to_args[Subversion-expected13-expected23-kwargs3] tests/unit/test_vcs.py::test_rev_options_to_args[Git-expected14-expected24-kwargs4] tests/unit/test_vcs.py::test_rev_options_to_display tests/unit/test_vcs.py::test_rev_options_make_new tests/unit/test_vcs.py::test_looks_like_hash tests/unit/test_vcs.py::test_should_add_vcs_url_prefix[Git-git://example.com/MyProject-False] tests/unit/test_vcs.py::test_should_add_vcs_url_prefix[Git-http://example.com/MyProject-True] tests/unit/test_vcs.py::test_should_add_vcs_url_prefix[Subversion-svn://example.com/MyProject-True] tests/unit/test_vcs.py::test_git_get_src_requirements tests/unit/test_vcs.py::test_git_resolve_revision_rev_exists tests/unit/test_vcs.py::test_git_resolve_revision_rev_not_found tests/unit/test_vcs.py::test_git_resolve_revision_not_found_warning tests/unit/test_vcs.py::test_git_is_commit_id_equal[5547fa909e83df8bd743d3978d6667497983a4b7-True] tests/unit/test_vcs.py::test_git_is_commit_id_equal[5547fa909-False] tests/unit/test_vcs.py::test_git_is_commit_id_equal[5678-False] tests/unit/test_vcs.py::test_git_is_commit_id_equal[abc123-False] tests/unit/test_vcs.py::test_git_is_commit_id_equal[foo-False] tests/unit/test_vcs.py::test_git_is_commit_id_equal[None-False] tests/unit/test_vcs.py::test_git__get_netloc_and_auth[args0-expected0] tests/unit/test_vcs.py::test_git__get_netloc_and_auth[args1-expected1] tests/unit/test_vcs.py::test_subversion__get_netloc_and_auth[args0-expected0] tests/unit/test_vcs.py::test_subversion__get_netloc_and_auth[args1-expected1] tests/unit/test_vcs.py::test_subversion__get_netloc_and_auth[args2-expected2] tests/unit/test_vcs.py::test_subversion__get_netloc_and_auth[args3-expected3] tests/unit/test_vcs.py::test_subversion__get_netloc_and_auth[args4-expected4] tests/unit/test_vcs.py::test_git__get_url_rev__idempotent tests/unit/test_vcs.py::test_version_control__get_url_rev_and_auth[svn+https://svn.example.com/MyProject-expected0] tests/unit/test_vcs.py::test_version_control__get_url_rev_and_auth[svn+https://svn.example.com/My+Project-expected1] tests/unit/test_vcs.py::test_version_control__get_url_rev_and_auth__missing_plus[https://svn.example.com/MyProject] tests/unit/test_vcs.py::test_version_control__get_url_rev_and_auth__missing_plus[https://svn.example.com/My+Project] tests/unit/test_vcs.py::test_bazaar__get_url_rev_and_auth[bzr+http://bzr.myproject.org/MyProject/trunk/#egg=MyProject-http://bzr.myproject.org/MyProject/trunk/] tests/unit/test_vcs.py::test_bazaar__get_url_rev_and_auth[bzr+https://bzr.myproject.org/MyProject/trunk/#egg=MyProject-https://bzr.myproject.org/MyProject/trunk/] tests/unit/test_vcs.py::test_bazaar__get_url_rev_and_auth[bzr+ftp://bzr.myproject.org/MyProject/trunk/#egg=MyProject-ftp://bzr.myproject.org/MyProject/trunk/] tests/unit/test_vcs.py::test_bazaar__get_url_rev_and_auth[bzr+sftp://bzr.myproject.org/MyProject/trunk/#egg=MyProject-sftp://bzr.myproject.org/MyProject/trunk/] tests/unit/test_vcs.py::test_bazaar__get_url_rev_and_auth[bzr+lp:MyLaunchpadProject#egg=MyLaunchpadProject-lp:MyLaunchpadProject] tests/unit/test_vcs.py::test_bazaar__get_url_rev_and_auth[bzr+ssh://bzr.myproject.org/MyProject/trunk/#egg=MyProject-bzr+ssh://bzr.myproject.org/MyProject/trunk/] tests/unit/test_vcs.py::test_subversion__get_url_rev_and_auth[svn+https://svn.example.com/MyProject#egg=MyProject-expected0] tests/unit/test_vcs.py::test_subversion__get_url_rev_and_auth[svn+https://user:pass@svn.example.com/MyProject#egg=MyProject-expected1] tests/unit/test_vcs.py::test_subversion__get_url_rev_and_auth[svn+ssh://svn.example.com/MyProject#egg=MyProject-expected2] tests/unit/test_vcs.py::test_subversion__get_url_rev_and_auth[svn+ssh://user@svn.example.com/MyProject#egg=MyProject-expected3] tests/unit/test_vcs.py::test_git__make_rev_args[None-None-expected0] tests/unit/test_vcs.py::test_git__make_rev_args[user-None-expected1] tests/unit/test_vcs.py::test_git__make_rev_args[user-pass-expected2] tests/unit/test_vcs.py::test_subversion__make_rev_args[None-None-expected0] tests/unit/test_vcs.py::test_subversion__make_rev_args[user-None-expected1] tests/unit/test_vcs.py::test_subversion__make_rev_args[user-pass-expected2] tests/unit/test_vcs.py::test_subversion__get_url_rev_options tests/unit/test_vcs.py::test_get_git_version tests/unit/test_vcs.py::test_subversion__init_use_interactive[None-False-False] tests/unit/test_vcs.py::test_subversion__init_use_interactive[None-True-True] tests/unit/test_vcs.py::test_subversion__init_use_interactive[False-False-False] tests/unit/test_vcs.py::test_subversion__init_use_interactive[False-True-False] tests/unit/test_vcs.py::test_subversion__init_use_interactive[True-False-True] tests/unit/test_vcs.py::test_subversion__init_use_interactive[True-True-True] tests/unit/test_vcs.py::test_subversion__call_vcs_version tests/unit/test_vcs.py::test_subversion__call_vcs_version_patched[svn, tests/unit/test_vcs.py::test_subversion__call_vcs_version_patched[svn tests/unit/test_vcs.py::test_subversion__call_vcs_version_patched[-expected_version8] tests/unit/test_vcs.py::test_subversion__call_vcs_version_svn_not_installed tests/unit/test_vcs.py::test_subversion__get_vcs_version_cached[version0] tests/unit/test_vcs.py::test_subversion__get_vcs_version_cached[version1] tests/unit/test_vcs.py::test_subversion__get_vcs_version_cached[version2] tests/unit/test_vcs.py::test_subversion__get_vcs_version_cached[version3] tests/unit/test_vcs.py::test_subversion__get_vcs_version_call_vcs[vcs_version0] tests/unit/test_vcs.py::test_subversion__get_vcs_version_call_vcs[vcs_version1] tests/unit/test_vcs.py::test_subversion__get_vcs_version_call_vcs[vcs_version2] tests/unit/test_vcs.py::test_subversion__get_remote_call_options[False-vcs_version0-expected_options0] tests/unit/test_vcs.py::test_subversion__get_remote_call_options[False-vcs_version1-expected_options1] tests/unit/test_vcs.py::test_subversion__get_remote_call_options[False-vcs_version2-expected_options2] tests/unit/test_vcs.py::test_subversion__get_remote_call_options[True-vcs_version3-expected_options3] tests/unit/test_vcs.py::test_subversion__get_remote_call_options[True-vcs_version4-expected_options4] tests/unit/test_vcs.py::test_subversion__get_remote_call_options[True-vcs_version5-expected_options5] tests/unit/test_vcs.py::TestSubversionArgs::test_export tests/unit/test_vcs.py::TestSubversionArgs::test_fetch_new tests/unit/test_vcs.py::TestSubversionArgs::test_fetch_new_revision tests/unit/test_vcs.py::TestSubversionArgs::test_obtain tests/unit/test_vcs.py::TestSubversionArgs::test_switch tests/unit/test_vcs.py::TestSubversionArgs::test_update
tests/unit/test_finder.py::TestCandidateEvaluator::test_evaluate_link__match[http:/yo/pytest-1.0.tar.gz-1.0] tests/unit/test_finder.py::TestCandidateEvaluator::test_evaluate_link__match[http:/yo/pytest-1.0-py2.py3-none-any.whl-1.0] tests/unit/test_finder.py::TestCandidateEvaluator::test_evaluate_link__substring_fails[http:/yo/pytest2-1.0.tar.gz-Missing tests/unit/test_finder.py::TestCandidateEvaluator::test_evaluate_link__substring_fails[http:/yo/pytest_xdist-1.0-py2.py3-none-any.whl-wrong tests/unit/test_utils.py::Tests_EgglinkPath::test_egglink_in_usersite_notvenv tests/unit/test_utils.py::Tests_EgglinkPath::test_egglink_in_usersite_venv_noglobal tests/unit/test_utils.py::Tests_EgglinkPath::test_egglink_in_usersite_venv_global tests/unit/test_utils.py::Tests_EgglinkPath::test_egglink_in_sitepkgs_notvenv tests/unit/test_utils.py::Tests_EgglinkPath::test_egglink_in_sitepkgs_venv_noglobal tests/unit/test_utils.py::Tests_EgglinkPath::test_egglink_in_sitepkgs_venv_global tests/unit/test_utils.py::Tests_EgglinkPath::test_egglink_in_both_notvenv tests/unit/test_utils.py::Tests_EgglinkPath::test_egglink_in_both_venv_noglobal tests/unit/test_utils.py::Tests_EgglinkPath::test_egglink_in_both_venv_global tests/unit/test_utils.py::Tests_EgglinkPath::test_noegglink_in_sitepkgs_notvenv tests/unit/test_utils.py::Tests_EgglinkPath::test_noegglink_in_sitepkgs_venv_noglobal tests/unit/test_utils.py::Tests_EgglinkPath::test_noegglink_in_sitepkgs_venv_global tests/unit/test_utils.py::TestUnpackArchives::test_unpack_tgz tests/unit/test_utils.py::TestUnpackArchives::test_unpack_zip
news/5671.bugfix src/pip/_internal/utils/setuptools_build.py news/1890.bugfix docs/html/reference/pip_install.rst news/1234.trivial src/pip/_internal/index.py src/pip/_internal/req/req_install.py src/pip/_internal/wheel.py src/pip/_internal/utils/misc.py src/pip/_internal/utils/compat.py src/pip/_internal/utils/ui.py src/pip/_internal/models/target_python.py news/4733.doc
news/5671.bugfix src/pip/_internal/utils/setuptools_build.py news/1890.bugfix docs/html/reference/pip_install.rst news/1234.trivial src/pip/_internal/index.py src/pip/_internal/req/req_install.py src/pip/_internal/wheel.py src/pip/_internal/utils/misc.py src/pip/_internal/utils/compat.py src/pip/_internal/utils/ui.py src/pip/_internal/models/target_python.py news/4733.doc