From 4628cb7fd37a1f91c8b9301774e02bfc703b96f8 Mon Sep 17 00:00:00 2001 From: openeuler-ci-bot <80474298@qq.com> Date: Thu, 24 Dec 2020 01:58:53 +0800 Subject: [PATCH 1/2] [patch tracking] 20201224015849754669 - https://github.com/pypa/pip/commit/1eebb12550cf4e207dc654b57553efe818792747 --- ...b12550cf4e207dc654b57553efe818792747.patch | 2284 +++++++++++++++++ 1 file changed, 2284 insertions(+) create mode 100644 1eebb12550cf4e207dc654b57553efe818792747.patch diff --git a/1eebb12550cf4e207dc654b57553efe818792747.patch b/1eebb12550cf4e207dc654b57553efe818792747.patch new file mode 100644 index 0000000..b08420e --- /dev/null +++ b/1eebb12550cf4e207dc654b57553efe818792747.patch @@ -0,0 +1,2284 @@ +diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml +index 58a95bbfba..636fdfd3d4 100644 +--- a/.pre-commit-config.yaml ++++ b/.pre-commit-config.yaml +@@ -74,10 +74,6 @@ repos: + - id: mypy + exclude: docs|tests + args: ["--pretty"] +- - id: mypy +- name: mypy, for Python 2 +- exclude: noxfile.py|tools/automation/release|docs|tests +- args: ["--pretty", "-2"] + + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.6.0 +diff --git a/docs/html/development/getting-started.rst b/docs/html/development/getting-started.rst +index 94ff37fc32..1d625613b7 100644 +--- a/docs/html/development/getting-started.rst ++++ b/docs/html/development/getting-started.rst +@@ -79,7 +79,7 @@ To run tests without parallelization, run: + $ tox -e py36 + + The example above runs tests against Python 3.6. You can also use other +-versions like ``py27`` and ``pypy3``. ++versions like ``py39`` and ``pypy3``. + + ``tox`` has been configured to forward any additional arguments it is given to + ``pytest``. This enables the use of pytest's `rich CLI`_. As an example, you +diff --git a/docs/html/development/release-process.rst b/docs/html/development/release-process.rst +index 17d11e7d1c..a133e57f20 100644 +--- a/docs/html/development/release-process.rst ++++ b/docs/html/development/release-process.rst +@@ -173,4 +173,3 @@ order to create one of these the changes should already be merged into the + .. _`get-pip repository`: https://github.com/pypa/get-pip + .. _`psf-salt repository`: https://github.com/python/psf-salt + .. _`CPython`: https://github.com/python/cpython +-.. _`CPython 2.7 EOL date`: https://www.python.org/doc/sunset-python-2/ +diff --git a/docs/html/reference/pip_install.rst b/docs/html/reference/pip_install.rst +index 1a5507fdc0..1b53513266 100644 +--- a/docs/html/reference/pip_install.rst ++++ b/docs/html/reference/pip_install.rst +@@ -291,7 +291,7 @@ Since version 6.0, pip also supports specifiers containing `environment markers + + :: + +- SomeProject ==5.4 ; python_version < '2.7' ++ SomeProject ==5.4 ; python_version < '3.8' + SomeProject; sys_platform == 'win32' + + Since version 19.1, pip also supports `direct references +diff --git a/docs/html/reference/pip_uninstall.rst b/docs/html/reference/pip_uninstall.rst +index f1c69d09c3..e6eeb5ebf6 100644 +--- a/docs/html/reference/pip_uninstall.rst ++++ b/docs/html/reference/pip_uninstall.rst +@@ -41,8 +41,8 @@ Examples + + $ python -m pip uninstall simplejson + Uninstalling simplejson: +- /home/me/env/lib/python2.7/site-packages/simplejson +- /home/me/env/lib/python2.7/site-packages/simplejson-2.2.1-py2.7.egg-info ++ /home/me/env/lib/python3.9/site-packages/simplejson ++ /home/me/env/lib/python3.9/site-packages/simplejson-2.2.1-py3.9.egg-info + Proceed (y/n)? y + Successfully uninstalled simplejson + +@@ -52,7 +52,7 @@ Examples + + C:\> py -m pip uninstall simplejson + Uninstalling simplejson: +- /home/me/env/lib/python2.7/site-packages/simplejson +- /home/me/env/lib/python2.7/site-packages/simplejson-2.2.1-py2.7.egg-info ++ /home/me/env/lib/python3.9/site-packages/simplejson ++ /home/me/env/lib/python3.9/site-packages/simplejson-2.2.1-py3.9.egg-info + Proceed (y/n)? y + Successfully uninstalled simplejson +diff --git a/news/8802.removal.rst b/news/8802.removal.rst +new file mode 100644 +index 0000000000..79d8e50816 +--- /dev/null ++++ b/news/8802.removal.rst +@@ -0,0 +1 @@ ++Modernise the codebase after Python 2. +diff --git a/src/pip/_internal/cli/base_command.py b/src/pip/_internal/cli/base_command.py +index ce93552b3c..190c4d86e6 100644 +--- a/src/pip/_internal/cli/base_command.py ++++ b/src/pip/_internal/cli/base_command.py +@@ -9,8 +9,6 @@ + import sys + import traceback + +-from pip._vendor.six import PY2 +- + from pip._internal.cli import cmdoptions + from pip._internal.cli.command_context import CommandContextMixIn + from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter +@@ -183,7 +181,7 @@ def _main(self, args): + issue=8333, + ) + +- if '2020-resolver' in options.features_enabled and not PY2: ++ if '2020-resolver' in options.features_enabled: + logger.warning( + "--use-feature=2020-resolver no longer has any effect, " + "since it is now the default dependency resolver in pip. " +diff --git a/src/pip/_internal/cli/parser.py b/src/pip/_internal/cli/parser.py +index 7170bfd384..b64e967806 100644 +--- a/src/pip/_internal/cli/parser.py ++++ b/src/pip/_internal/cli/parser.py +@@ -7,16 +7,15 @@ + + import logging + import optparse ++import shutil + import sys + import textwrap + from distutils.util import strtobool + + from pip._vendor.contextlib2 import suppress +-from pip._vendor.six import string_types + + from pip._internal.cli.status_codes import UNKNOWN_ERROR + from pip._internal.configuration import Configuration, ConfigurationError +-from pip._internal.utils.compat import get_terminal_size + from pip._internal.utils.misc import redact_auth_from_url + + logger = logging.getLogger(__name__) +@@ -29,7 +28,7 @@ def __init__(self, *args, **kwargs): + # help position must be aligned with __init__.parseopts.description + kwargs['max_help_position'] = 30 + kwargs['indent_increment'] = 1 +- kwargs['width'] = get_terminal_size()[0] - 2 ++ kwargs['width'] = shutil.get_terminal_size()[0] - 2 + optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs) + + def format_option_strings(self, option): +@@ -119,7 +118,7 @@ def expand_default(self, option): + help_text = optparse.IndentedHelpFormatter.expand_default(self, option) + + if default_values and option.metavar == 'URL': +- if isinstance(default_values, string_types): ++ if isinstance(default_values, str): + default_values = [default_values] + + # If its not a list, we should abort and just return the help text +@@ -275,7 +274,7 @@ def get_default_values(self): + defaults = self._update_defaults(self.defaults.copy()) # ours + for option in self._get_all_options(): + default = defaults.get(option.dest) +- if isinstance(default, string_types): ++ if isinstance(default, str): + opt_str = option.get_opt_string() + defaults[option.dest] = option.check_value(opt_str, default) + return optparse.Values(defaults) +diff --git a/src/pip/_internal/cli/progress_bars.py b/src/pip/_internal/cli/progress_bars.py +index 69338552f1..968a8c7cf3 100644 +--- a/src/pip/_internal/cli/progress_bars.py ++++ b/src/pip/_internal/cli/progress_bars.py +@@ -4,7 +4,6 @@ + import sys + from signal import SIGINT, default_int_handler, signal + +-from pip._vendor import six + from pip._vendor.progress.bar import Bar, FillingCirclesBar, IncrementalBar + from pip._vendor.progress.spinner import Spinner + +@@ -36,8 +35,8 @@ def _select_progress_class(preferred, fallback): + # Collect all of the possible characters we want to use with the preferred + # bar. + characters = [ +- getattr(preferred, "empty_fill", six.text_type()), +- getattr(preferred, "fill", six.text_type()), ++ getattr(preferred, "empty_fill", ""), ++ getattr(preferred, "fill", ""), + ] + characters += list(getattr(preferred, "phases", [])) + +@@ -45,7 +44,7 @@ def _select_progress_class(preferred, fallback): + # of the given file, if this works then we'll assume that we can use the + # fancier bar and if not we'll fall back to the plaintext bar. + try: +- six.text_type().join(characters).encode(encoding) ++ "".join(characters).encode(encoding) + except UnicodeEncodeError: + return fallback + else: +diff --git a/src/pip/_internal/cli/req_command.py b/src/pip/_internal/cli/req_command.py +index 008066ab1c..2adbb74119 100644 +--- a/src/pip/_internal/cli/req_command.py ++++ b/src/pip/_internal/cli/req_command.py +@@ -9,8 +9,6 @@ + import os + from functools import partial + +-from pip._vendor.six import PY2 +- + from pip._internal.cli import cmdoptions + from pip._internal.cli.base_command import Command + from pip._internal.cli.command_context import CommandContextMixIn +@@ -200,14 +198,6 @@ def __init__(self, *args, **kw): + def determine_resolver_variant(options): + # type: (Values) -> str + """Determines which resolver should be used, based on the given options.""" +- # We didn't want to change things for Python 2, since it's nearly done with +- # and we're using performance improvements that only work on Python 3. +- if PY2: +- if '2020-resolver' in options.features_enabled: +- return "2020-resolver" +- else: +- return "legacy" +- + if "legacy-resolver" in options.deprecated_features_enabled: + return "legacy" + +diff --git a/src/pip/_internal/commands/list.py b/src/pip/_internal/commands/list.py +index 27b15d70a5..3f29e48b4f 100644 +--- a/src/pip/_internal/commands/list.py ++++ b/src/pip/_internal/commands/list.py +@@ -3,8 +3,6 @@ + import json + import logging + +-from pip._vendor import six +- + from pip._internal.cli import cmdoptions + from pip._internal.cli.req_command import IndexGroupCommand + from pip._internal.cli.status_codes import SUCCESS +@@ -315,13 +313,13 @@ def format_for_json(packages, options): + for dist in packages: + info = { + 'name': dist.project_name, +- 'version': six.text_type(dist.version), ++ 'version': str(dist.version), + } + if options.verbose >= 1: + info['location'] = dist.location + info['installer'] = get_installer(dist) + if options.outdated: +- info['latest_version'] = six.text_type(dist.latest_version) ++ info['latest_version'] = str(dist.latest_version) + info['latest_filetype'] = dist.latest_filetype + data.append(info) + return json.dumps(data) +diff --git a/src/pip/_internal/commands/search.py b/src/pip/_internal/commands/search.py +index 146d653e55..f382ddc651 100644 +--- a/src/pip/_internal/commands/search.py ++++ b/src/pip/_internal/commands/search.py +@@ -1,6 +1,7 @@ + from __future__ import absolute_import + + import logging ++import shutil + import sys + import textwrap + from collections import OrderedDict +@@ -18,7 +19,6 @@ + from pip._internal.exceptions import CommandError + from pip._internal.models.index import PyPI + from pip._internal.network.xmlrpc import PipXmlrpcTransport +-from pip._internal.utils.compat import get_terminal_size + from pip._internal.utils.logging import indent_log + from pip._internal.utils.misc import get_distribution, write_output + from pip._internal.utils.typing import MYPY_CHECK_RUNNING +@@ -64,7 +64,7 @@ def run(self, options, args): + + terminal_width = None + if sys.stdout.isatty(): +- terminal_width = get_terminal_size()[0] ++ terminal_width = shutil.get_terminal_size()[0] + + print_results(hits, terminal_width=terminal_width) + if pypi_hits: +diff --git a/src/pip/_internal/configuration.py b/src/pip/_internal/configuration.py +index 23614fd2bb..9e83578556 100644 +--- a/src/pip/_internal/configuration.py ++++ b/src/pip/_internal/configuration.py +@@ -11,13 +11,12 @@ + A single word describing where the configuration key-value pair came from + """ + ++import configparser + import locale + import logging + import os + import sys + +-from pip._vendor.six.moves import configparser +- + from pip._internal.exceptions import ( + ConfigurationError, + ConfigurationFileCouldNotBeLoaded, +diff --git a/src/pip/_internal/distributions/base.py b/src/pip/_internal/distributions/base.py +index 3a789f8043..dc7ae96aa0 100644 +--- a/src/pip/_internal/distributions/base.py ++++ b/src/pip/_internal/distributions/base.py +@@ -1,7 +1,5 @@ + import abc + +-from pip._vendor.six import add_metaclass +- + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + + if MYPY_CHECK_RUNNING: +@@ -13,8 +11,7 @@ + from pip._internal.req import InstallRequirement + + +-@add_metaclass(abc.ABCMeta) +-class AbstractDistribution(object): ++class AbstractDistribution(object, metaclass=abc.ABCMeta): + """A base class for handling installable artifacts. + + The requirements for anything installable are as follows: +@@ -29,7 +26,6 @@ class AbstractDistribution(object): + - we must be able to create a Distribution object exposing the + above metadata. + """ +- + def __init__(self, req): + # type: (InstallRequirement) -> None + super(AbstractDistribution, self).__init__() +diff --git a/src/pip/_internal/exceptions.py b/src/pip/_internal/exceptions.py +index 56482caf77..870205584a 100644 +--- a/src/pip/_internal/exceptions.py ++++ b/src/pip/_internal/exceptions.py +@@ -4,25 +4,18 @@ + + from itertools import chain, groupby, repeat + +-from pip._vendor.six import iteritems +- + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + + if MYPY_CHECK_RUNNING: ++ import configparser ++ from hashlib import _Hash + from typing import Any, Dict, List, Optional, Text + + from pip._vendor.pkg_resources import Distribution + from pip._vendor.requests.models import Request, Response +- from pip._vendor.six import PY3 +- from pip._vendor.six.moves import configparser + + from pip._internal.req.req_install import InstallRequirement + +- if PY3: +- from hashlib import _Hash +- else: +- from hashlib import _hash as _Hash +- + + class PipError(Exception): + """Base pip exception""" +@@ -346,7 +339,7 @@ def hash_then_or(hash_name): + return chain([hash_name], repeat(' or')) + + lines = [] # type: List[str] +- for hash_name, expecteds in iteritems(self.allowed): ++ for hash_name, expecteds in self.allowed.items(): + prefix = hash_then_or(hash_name) + lines.extend((' Expected {} {}'.format(next(prefix), e)) + for e in expecteds) +diff --git a/src/pip/_internal/index/collector.py b/src/pip/_internal/index/collector.py +index b850b8cbed..b852645fd8 100644 +--- a/src/pip/_internal/index/collector.py ++++ b/src/pip/_internal/index/collector.py +@@ -10,12 +10,12 @@ + import os + import re + from collections import OrderedDict ++from urllib import parse as urllib_parse ++from urllib import request as urllib_request + + from pip._vendor import html5lib, requests + from pip._vendor.distlib.compat import unescape + from pip._vendor.requests.exceptions import RetryError, SSLError +-from pip._vendor.six.moves.urllib import parse as urllib_parse +-from pip._vendor.six.moves.urllib import request as urllib_request + + from pip._internal.exceptions import NetworkConnectionError + from pip._internal.models.link import Link +diff --git a/src/pip/_internal/models/direct_url.py b/src/pip/_internal/models/direct_url.py +index 99aa68d121..8f544caf60 100644 +--- a/src/pip/_internal/models/direct_url.py ++++ b/src/pip/_internal/models/direct_url.py +@@ -1,9 +1,7 @@ + """ PEP 610 """ + import json + import re +- +-from pip._vendor import six +-from pip._vendor.six.moves.urllib import parse as urllib_parse ++from urllib import parse as urllib_parse + + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +@@ -35,8 +33,6 @@ def _get(d, expected_type, key, default=None): + if key not in d: + return default + value = d[key] +- if six.PY2 and expected_type is str: +- expected_type = six.string_types # type: ignore + if not isinstance(value, expected_type): + raise DirectUrlValidationError( + "{!r} has unexpected type for {} (expected {})".format( +diff --git a/src/pip/_internal/models/index.py b/src/pip/_internal/models/index.py +index 5b4a1fe227..0374d7f55e 100644 +--- a/src/pip/_internal/models/index.py ++++ b/src/pip/_internal/models/index.py +@@ -1,4 +1,4 @@ +-from pip._vendor.six.moves.urllib import parse as urllib_parse ++from urllib import parse as urllib_parse + + + class PackageIndex(object): +diff --git a/src/pip/_internal/models/link.py b/src/pip/_internal/models/link.py +index 29ef402bee..4ad4f7bde9 100644 +--- a/src/pip/_internal/models/link.py ++++ b/src/pip/_internal/models/link.py +@@ -1,8 +1,7 @@ + import os + import posixpath + import re +- +-from pip._vendor.six.moves.urllib import parse as urllib_parse ++from urllib import parse as urllib_parse + + from pip._internal.utils.filetypes import WHEEL_EXTENSION + from pip._internal.utils.misc import ( +diff --git a/src/pip/_internal/models/search_scope.py b/src/pip/_internal/models/search_scope.py +index d732504e6f..ab6f914869 100644 +--- a/src/pip/_internal/models/search_scope.py ++++ b/src/pip/_internal/models/search_scope.py +@@ -2,9 +2,9 @@ + import logging + import os + import posixpath ++from urllib import parse as urllib_parse + + from pip._vendor.packaging.utils import canonicalize_name +-from pip._vendor.six.moves.urllib import parse as urllib_parse + + from pip._internal.models.index import PyPI + from pip._internal.utils.compat import has_tls +diff --git a/src/pip/_internal/network/auth.py b/src/pip/_internal/network/auth.py +index 357811a16f..3de21518e9 100644 +--- a/src/pip/_internal/network/auth.py ++++ b/src/pip/_internal/network/auth.py +@@ -5,10 +5,10 @@ + """ + + import logging ++from urllib import parse as urllib_parse + + from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth + from pip._vendor.requests.utils import get_netrc_auth +-from pip._vendor.six.moves.urllib import parse as urllib_parse + + from pip._internal.utils.misc import ( + ask, +diff --git a/src/pip/_internal/network/lazy_wheel.py b/src/pip/_internal/network/lazy_wheel.py +index 608475abab..83704f6f19 100644 +--- a/src/pip/_internal/network/lazy_wheel.py ++++ b/src/pip/_internal/network/lazy_wheel.py +@@ -8,7 +8,6 @@ + from zipfile import BadZipfile, ZipFile + + from pip._vendor.requests.models import CONTENT_CHUNK_SIZE +-from pip._vendor.six.moves import range + + from pip._internal.network.utils import HEADERS, raise_for_status, response_chunks + from pip._internal.utils.typing import MYPY_CHECK_RUNNING +diff --git a/src/pip/_internal/network/session.py b/src/pip/_internal/network/session.py +index 454945d9ae..5839c4d28d 100644 +--- a/src/pip/_internal/network/session.py ++++ b/src/pip/_internal/network/session.py +@@ -13,13 +13,13 @@ + import platform + import sys + import warnings ++from urllib import parse as urllib_parse + + from pip._vendor import requests, six, urllib3 + from pip._vendor.cachecontrol import CacheControlAdapter + from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter + from pip._vendor.requests.models import Response + from pip._vendor.requests.structures import CaseInsensitiveDict +-from pip._vendor.six.moves.urllib import parse as urllib_parse + from pip._vendor.urllib3.exceptions import InsecureRequestWarning + + from pip import __version__ +diff --git a/src/pip/_internal/network/xmlrpc.py b/src/pip/_internal/network/xmlrpc.py +index 504018f28f..d025a145a3 100644 +--- a/src/pip/_internal/network/xmlrpc.py ++++ b/src/pip/_internal/network/xmlrpc.py +@@ -2,11 +2,11 @@ + """ + + import logging ++from urllib import parse as urllib_parse + + # NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is + # why we ignore the type on this import + from pip._vendor.six.moves import xmlrpc_client # type: ignore +-from pip._vendor.six.moves.urllib import parse as urllib_parse + + from pip._internal.exceptions import NetworkConnectionError + from pip._internal.network.utils import raise_for_status +diff --git a/src/pip/_internal/operations/freeze.py b/src/pip/_internal/operations/freeze.py +index d4f790cd44..ba885afd65 100644 +--- a/src/pip/_internal/operations/freeze.py ++++ b/src/pip/_internal/operations/freeze.py +@@ -4,7 +4,6 @@ + import logging + import os + +-from pip._vendor import six + from pip._vendor.packaging.utils import canonicalize_name + from pip._vendor.pkg_resources import RequirementParseError + +@@ -162,7 +161,7 @@ def freeze( + + # Warn about requirements that were included multiple times (in a + # single requirements file or in different requirements files). +- for name, files in six.iteritems(req_files): ++ for name, files in req_files.items(): + if len(files) > 1: + logger.warning("Requirement %s included multiple times [%s]", + name, ', '.join(sorted(set(files)))) +diff --git a/src/pip/_internal/operations/install/wheel.py b/src/pip/_internal/operations/install/wheel.py +index 8b67ebb943..7b7d48661c 100644 +--- a/src/pip/_internal/operations/install/wheel.py ++++ b/src/pip/_internal/operations/install/wheel.py +@@ -15,14 +15,13 @@ + import sys + import warnings + from base64 import urlsafe_b64encode +-from itertools import chain, starmap ++from itertools import chain, filterfalse, starmap + from zipfile import ZipFile + + from pip._vendor import pkg_resources + from pip._vendor.distlib.scripts import ScriptMaker + from pip._vendor.distlib.util import get_export_entry +-from pip._vendor.six import PY2, ensure_str, ensure_text, itervalues, reraise, text_type +-from pip._vendor.six.moves import filterfalse, map ++from pip._vendor.six import ensure_str, ensure_text, reraise + + from pip._internal.exceptions import InstallationError + from pip._internal.locations import get_major_minor_version +@@ -70,12 +69,12 @@ + from pip._internal.models.scheme import Scheme + from pip._internal.utils.filesystem import NamedTemporaryFileResult + +- RecordPath = NewType('RecordPath', text_type) ++ RecordPath = NewType('RecordPath', str) + InstalledCSVRow = Tuple[RecordPath, str, Union[int, str]] + + class File(Protocol): + src_record_path = None # type: RecordPath +- dest_path = None # type: text_type ++ dest_path = None # type: str + changed = None # type: bool + + def save(self): +@@ -87,7 +86,7 @@ def save(self): + + + def rehash(path, blocksize=1 << 20): +- # type: (text_type, int) -> Tuple[str, str] ++ # type: (str, int) -> Tuple[str, str] + """Return (encoded_digest, length) for path using hashlib.sha256()""" + h, length = hash_file(path, blocksize) + digest = 'sha256=' + urlsafe_b64encode( +@@ -102,14 +101,11 @@ def csv_io_kwargs(mode): + """Return keyword arguments to properly open a CSV file + in the given mode. + """ +- if PY2: +- return {'mode': '{}b'.format(mode)} +- else: +- return {'mode': mode, 'newline': '', 'encoding': 'utf-8'} ++ return {'mode': mode, 'newline': '', 'encoding': 'utf-8'} + + + def fix_script(path): +- # type: (text_type) -> bool ++ # type: (str) -> bool + """Replace #!python with #!/path/to/python + Return True if file was changed. + """ +@@ -257,12 +253,12 @@ def _normalized_outrows(outrows): + + + def _record_to_fs_path(record_path): +- # type: (RecordPath) -> text_type ++ # type: (RecordPath) -> str + return record_path + + + def _fs_to_record_path(path, relative_to=None): +- # type: (text_type, Optional[text_type]) -> RecordPath ++ # type: (str, Optional[str]) -> RecordPath + if relative_to is not None: + # On Windows, do not handle relative paths if they belong to different + # logical disks +@@ -307,7 +303,7 @@ def get_csv_rows_for_installed( + path = _fs_to_record_path(f, lib_dir) + digest, length = rehash(f) + installed_rows.append((path, digest, length)) +- for installed_record_path in itervalues(installed): ++ for installed_record_path in installed.values(): + installed_rows.append((installed_record_path, '', '')) + return installed_rows + +@@ -400,7 +396,7 @@ def get_console_script_specs(console): + + class ZipBackedFile(object): + def __init__(self, src_record_path, dest_path, zip_file): +- # type: (RecordPath, text_type, ZipFile) -> None ++ # type: (RecordPath, str, ZipFile) -> None + self.src_record_path = src_record_path + self.dest_path = dest_path + self._zip_file = zip_file +@@ -408,12 +404,7 @@ def __init__(self, src_record_path, dest_path, zip_file): + + def _getinfo(self): + # type: () -> ZipInfo +- if not PY2: +- return self._zip_file.getinfo(self.src_record_path) +- # Python 2 does not expose a way to detect a ZIP's encoding, but the +- # wheel specification (PEP 427) explicitly mandates that paths should +- # use UTF-8, so we assume it is true. +- return self._zip_file.getinfo(self.src_record_path.encode("utf-8")) ++ return self._zip_file.getinfo(self.src_record_path) + + def save(self): + # type: () -> None +@@ -525,7 +516,7 @@ def _install_wheel( + generated = [] # type: List[str] + + def record_installed(srcfile, destfile, modified=False): +- # type: (RecordPath, text_type, bool) -> None ++ # type: (RecordPath, str, bool) -> None + """Map archive RECORD paths to installation RECORD paths.""" + newpath = _fs_to_record_path(destfile, lib_dir) + installed[srcfile] = newpath +@@ -546,7 +537,7 @@ def is_dir_path(path): + return path.endswith("/") + + def assert_no_path_traversal(dest_dir_path, target_path): +- # type: (text_type, text_type) -> None ++ # type: (str, str) -> None + if not is_within_directory(dest_dir_path, target_path): + message = ( + "The wheel {!r} has a file {!r} trying to install" +@@ -557,7 +548,7 @@ def assert_no_path_traversal(dest_dir_path, target_path): + ) + + def root_scheme_file_maker(zip_file, dest): +- # type: (ZipFile, text_type) -> Callable[[RecordPath], File] ++ # type: (ZipFile, str) -> Callable[[RecordPath], File] + def make_root_scheme_file(record_path): + # type: (RecordPath) -> File + normed_path = os.path.normpath(record_path) +@@ -675,7 +666,7 @@ def is_entrypoint_wrapper(file): + record_installed(file.src_record_path, file.dest_path, file.changed) + + def pyc_source_file_paths(): +- # type: () -> Iterator[text_type] ++ # type: () -> Iterator[str] + # We de-duplicate installation paths, since there can be overlap (e.g. + # file in .data maps to same location as file in wheel root). + # Sorting installation paths makes it easier to reproduce and debug +@@ -689,16 +680,10 @@ def pyc_source_file_paths(): + yield full_installed_path + + def pyc_output_path(path): +- # type: (text_type) -> text_type ++ # type: (str) -> str + """Return the path the pyc file would have been written to. + """ +- if PY2: +- if sys.flags.optimize: +- return path + 'o' +- else: +- return path + 'c' +- else: +- return importlib.util.cache_from_source(path) ++ return importlib.util.cache_from_source(path) + + # Compile all of the pyc files for the installed files + if pycompile: +diff --git a/src/pip/_internal/operations/prepare.py b/src/pip/_internal/operations/prepare.py +index 13b2c0beee..f1321709b8 100644 +--- a/src/pip/_internal/operations/prepare.py ++++ b/src/pip/_internal/operations/prepare.py +@@ -10,7 +10,6 @@ + import shutil + + from pip._vendor.packaging.utils import canonicalize_name +-from pip._vendor.six import PY2 + + from pip._internal.distributions import make_distribution_for_install_requirement + from pip._internal.distributions.installed import InstalledDistribution +@@ -51,26 +50,16 @@ + from pip._internal.req.req_tracker import RequirementTracker + from pip._internal.utils.hashes import Hashes + +- if PY2: +- CopytreeKwargs = TypedDict( +- 'CopytreeKwargs', +- { +- 'ignore': Callable[[str, List[str]], List[str]], +- 'symlinks': bool, +- }, +- total=False, +- ) +- else: +- CopytreeKwargs = TypedDict( +- 'CopytreeKwargs', +- { +- 'copy_function': Callable[[str, str], None], +- 'ignore': Callable[[str, List[str]], List[str]], +- 'ignore_dangling_symlinks': bool, +- 'symlinks': bool, +- }, +- total=False, +- ) ++ CopytreeKwargs = TypedDict( ++ 'CopytreeKwargs', ++ { ++ 'copy_function': Callable[[str, str], None], ++ 'ignore': Callable[[str, List[str]], List[str]], ++ 'ignore_dangling_symlinks': bool, ++ 'symlinks': bool, ++ }, ++ total=False, ++ ) + + logger = logging.getLogger(__name__) + +@@ -177,14 +166,13 @@ def ignore(d, names): + skipped += [target_basename] + return skipped + +- kwargs = dict(ignore=ignore, symlinks=True) # type: CopytreeKwargs +- +- if not PY2: +- # Python 2 does not support copy_function, so we only ignore +- # errors on special file copy in Python 3. +- kwargs['copy_function'] = _copy2_ignoring_special_files +- +- shutil.copytree(source, target, **kwargs) ++ shutil.copytree( ++ source, ++ target, ++ ignore=ignore, ++ symlinks=True, ++ copy_function=_copy2_ignoring_special_files, ++ ) + + + def get_file_url( +diff --git a/src/pip/_internal/pyproject.py b/src/pip/_internal/pyproject.py +index 4144a9ed60..ee90de12e1 100644 +--- a/src/pip/_internal/pyproject.py ++++ b/src/pip/_internal/pyproject.py +@@ -2,10 +2,9 @@ + + import io + import os +-import sys + from collections import namedtuple + +-from pip._vendor import six, toml ++from pip._vendor import toml + from pip._vendor.packaging.requirements import InvalidRequirement, Requirement + + from pip._internal.exceptions import InstallationError +@@ -19,19 +18,13 @@ def _is_list_of_str(obj): + # type: (Any) -> bool + return ( + isinstance(obj, list) and +- all(isinstance(item, six.string_types) for item in obj) ++ all(isinstance(item, str) for item in obj) + ) + + + def make_pyproject_path(unpacked_source_directory): + # type: (str) -> str +- path = os.path.join(unpacked_source_directory, 'pyproject.toml') +- +- # Python2 __file__ should not be unicode +- if six.PY2 and isinstance(path, six.text_type): +- path = path.encode(sys.getfilesystemencoding()) +- +- return path ++ return os.path.join(unpacked_source_directory, 'pyproject.toml') + + + BuildSystemDetails = namedtuple('BuildSystemDetails', [ +diff --git a/src/pip/_internal/req/req_file.py b/src/pip/_internal/req/req_file.py +index 0af60fa056..ae891ce7aa 100644 +--- a/src/pip/_internal/req/req_file.py ++++ b/src/pip/_internal/req/req_file.py +@@ -8,9 +8,7 @@ + import os + import re + import shlex +-import sys +- +-from pip._vendor.six.moves.urllib import parse as urllib_parse ++from urllib import parse as urllib_parse + + from pip._internal.cli import cmdoptions + from pip._internal.exceptions import InstallationError, RequirementsFileParseError +@@ -410,10 +408,6 @@ def parse_line(line): + defaults.format_control = finder.format_control + + args_str, options_str = break_args_options(line) +- # Prior to 2.7.3, shlex cannot deal with unicode entries +- if sys.version_info < (2, 7, 3): +- # https://github.com/python/mypy/issues/1174 +- options_str = options_str.encode('utf8') # type: ignore + + # https://github.com/python/mypy/issues/1174 + opts, _ = parser.parse_args( +diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py +index 866d18fcb6..067affd53a 100644 +--- a/src/pip/_internal/req/req_install.py ++++ b/src/pip/_internal/req/req_install.py +@@ -223,7 +223,7 @@ def __str__(self): + if self.satisfied_by is not None: + s += ' in {}'.format(display_path(self.satisfied_by.location)) + if self.comes_from: +- if isinstance(self.comes_from, six.string_types): ++ if isinstance(self.comes_from, str): + comes_from = self.comes_from # type: Optional[str] + else: + comes_from = self.comes_from.from_path() +@@ -334,7 +334,7 @@ def from_path(self): + return None + s = str(self.req) + if self.comes_from: +- if isinstance(self.comes_from, six.string_types): ++ if isinstance(self.comes_from, str): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() +@@ -480,10 +480,6 @@ def setup_py_path(self): + assert self.source_dir, "No source dir for {}".format(self) + setup_py = os.path.join(self.unpacked_source_directory, 'setup.py') + +- # Python2 __file__ should not be unicode +- if six.PY2 and isinstance(setup_py, six.text_type): +- setup_py = setup_py.encode(sys.getfilesystemencoding()) +- + return setup_py + + @property +diff --git a/src/pip/_internal/req/req_uninstall.py b/src/pip/_internal/req/req_uninstall.py +index 2e7dfcc736..b70d5e7f43 100644 +--- a/src/pip/_internal/req/req_uninstall.py ++++ b/src/pip/_internal/req/req_uninstall.py +@@ -6,12 +6,13 @@ + import os + import sys + import sysconfig ++from importlib.util import cache_from_source + + from pip._vendor import pkg_resources + + from pip._internal.exceptions import UninstallationError + from pip._internal.locations import bin_py, bin_user +-from pip._internal.utils.compat import WINDOWS, cache_from_source, uses_pycache ++from pip._internal.utils.compat import WINDOWS + from pip._internal.utils.logging import indent_log + from pip._internal.utils.misc import ( + FakeFile, +@@ -363,7 +364,7 @@ def add(self, path): + + # __pycache__ files can show up after 'installed-files.txt' is created, + # due to imports +- if os.path.splitext(path)[1] == '.py' and uses_pycache: ++ if os.path.splitext(path)[1] == '.py': + self.add(cache_from_source(path)) + + def add_pth(self, pth_file, entry): +@@ -609,7 +610,7 @@ def add(self, entry): + # treats non-absolute paths with drive letter markings like c:foo\bar + # as absolute paths. It also does not recognize UNC paths if they don't + # have more than "\\sever\share". Valid examples: "\\server\share\" or +- # "\\server\share\folder". Python 2.7.8+ support UNC in splitdrive. ++ # "\\server\share\folder". + if WINDOWS and not os.path.splitdrive(entry)[0]: + entry = entry.replace('\\', '/') + self.entries.add(entry) +diff --git a/src/pip/_internal/utils/compat.py b/src/pip/_internal/utils/compat.py +index 2196e6e0ae..e6ddbcb5f8 100644 +--- a/src/pip/_internal/utils/compat.py ++++ b/src/pip/_internal/utils/compat.py +@@ -11,15 +11,12 @@ + import locale + import logging + import os +-import shutil + import sys + +-from pip._vendor.six import PY2, text_type +- + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + + if MYPY_CHECK_RUNNING: +- from typing import Callable, Optional, Protocol, Text, Tuple, TypeVar, Union ++ from typing import Callable, Optional, Protocol, Text, TypeVar, Union + + # Used in the @lru_cache polyfill. + F = TypeVar('F') +@@ -41,47 +38,13 @@ def __call__(self, maxsize=None): + + + __all__ = [ +- "ipaddress", "uses_pycache", "console_to_str", +- "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", "get_terminal_size", ++ "ipaddress", "console_to_str", ++ "get_path_uid", "stdlib_pkgs", "WINDOWS", + ] + + + logger = logging.getLogger(__name__) + +-if PY2: +- import imp +- +- try: +- cache_from_source = imp.cache_from_source # type: ignore +- except AttributeError: +- # does not use __pycache__ +- cache_from_source = None +- +- uses_pycache = cache_from_source is not None +-else: +- uses_pycache = True +- from importlib.util import cache_from_source +- +- +-if PY2: +- # In Python 2.7, backslashreplace exists +- # but does not support use for decoding. +- # We implement our own replace handler for this +- # situation, so that we can consistently use +- # backslash replacement for all versions. +- def backslashreplace_decode_fn(err): +- raw_bytes = (err.object[i] for i in range(err.start, err.end)) +- # Python 2 gave us characters - convert to numeric bytes +- raw_bytes = (ord(b) for b in raw_bytes) +- return u"".join(map(u"\\x{:x}".format, raw_bytes)), err.end +- codecs.register_error( +- "backslashreplace_decode", +- backslashreplace_decode_fn, +- ) +- backslashreplace_decode = "backslashreplace_decode" +-else: +- backslashreplace_decode = "backslashreplace" +- + + def has_tls(): + # type: () -> bool +@@ -114,7 +77,7 @@ def str_to_display(data, desc=None): + We also ensure that the output can be safely written to standard output + without encoding errors. + """ +- if isinstance(data, text_type): ++ if isinstance(data, str): + return data + + # Otherwise, data is a bytes object (str in Python 2). +@@ -135,7 +98,7 @@ def str_to_display(data, desc=None): + desc or 'Bytes object', + encoding, + ) +- decoded_data = data.decode(encoding, errors=backslashreplace_decode) ++ decoded_data = data.decode(encoding, errors="backslashreplace") + + # Make sure we can print the output, by encoding it to the output + # encoding with replacement of unencodable characters, and then +@@ -226,60 +189,6 @@ def expanduser(path): + (sys.platform == 'cli' and os.name == 'nt')) + + +-def samefile(file1, file2): +- # type: (str, str) -> bool +- """Provide an alternative for os.path.samefile on Windows/Python2""" +- if hasattr(os.path, 'samefile'): +- return os.path.samefile(file1, file2) +- else: +- path1 = os.path.normcase(os.path.abspath(file1)) +- path2 = os.path.normcase(os.path.abspath(file2)) +- return path1 == path2 +- +- +-if hasattr(shutil, 'get_terminal_size'): +- def get_terminal_size(): +- # type: () -> Tuple[int, int] +- """ +- Returns a tuple (x, y) representing the width(x) and the height(y) +- in characters of the terminal window. +- """ +- return tuple(shutil.get_terminal_size()) # type: ignore +-else: +- def get_terminal_size(): +- # type: () -> Tuple[int, int] +- """ +- Returns a tuple (x, y) representing the width(x) and the height(y) +- in characters of the terminal window. +- """ +- def ioctl_GWINSZ(fd): +- try: +- import fcntl +- import struct +- import termios +- cr = struct.unpack_from( +- 'hh', +- fcntl.ioctl(fd, termios.TIOCGWINSZ, '12345678') +- ) +- except Exception: +- return None +- if cr == (0, 0): +- return None +- return cr +- cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) +- if not cr: +- if sys.platform != "win32": +- try: +- fd = os.open(os.ctermid(), os.O_RDONLY) +- cr = ioctl_GWINSZ(fd) +- os.close(fd) +- except Exception: +- pass +- if not cr: +- cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) +- return int(cr[1]), int(cr[0]) +- +- + # Fallback to noop_lru_cache in Python 2 + # TODO: this can be removed when python 2 support is dropped! + def noop_lru_cache(maxsize=None): +diff --git a/src/pip/_internal/utils/filesystem.py b/src/pip/_internal/utils/filesystem.py +index 303243fd22..1b0c083cfd 100644 +--- a/src/pip/_internal/utils/filesystem.py ++++ b/src/pip/_internal/utils/filesystem.py +@@ -12,7 +12,6 @@ + # NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is + # why we ignore the type on this import. + from pip._vendor.retrying import retry # type: ignore +-from pip._vendor.six import PY2 + + from pip._internal.utils.compat import get_path_uid + from pip._internal.utils.misc import format_size +@@ -114,18 +113,7 @@ def adjacent_tmp_file(path, **kwargs): + + _replace_retry = retry(stop_max_delay=1000, wait_fixed=250) + +-if PY2: +- @_replace_retry +- def replace(src, dest): +- # type: (str, str) -> None +- try: +- os.rename(src, dest) +- except OSError: +- os.remove(dest) +- os.rename(src, dest) +- +-else: +- replace = _replace_retry(os.replace) ++replace = _replace_retry(os.replace) + + + # test_writable_dir and _test_writable_dir_win are copied from Flit, +diff --git a/src/pip/_internal/utils/hashes.py b/src/pip/_internal/utils/hashes.py +index 4d90f5bfda..53da6636e6 100644 +--- a/src/pip/_internal/utils/hashes.py ++++ b/src/pip/_internal/utils/hashes.py +@@ -2,21 +2,14 @@ + + import hashlib + +-from pip._vendor.six import iteritems, iterkeys, itervalues +- + from pip._internal.exceptions import HashMismatch, HashMissing, InstallationError + from pip._internal.utils.misc import read_chunks + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + + if MYPY_CHECK_RUNNING: ++ from hashlib import _Hash + from typing import BinaryIO, Dict, Iterator, List, NoReturn + +- from pip._vendor.six import PY3 +- if PY3: +- from hashlib import _Hash +- else: +- from hashlib import _hash as _Hash +- + + # The recommended hash algo of the moment. Change this whenever the state of + # the art changes; it won't hurt backward compatibility. +@@ -60,7 +53,7 @@ def __and__(self, other): + + # Otherwise only hashes that present in both objects are allowed. + new = {} +- for alg, values in iteritems(other._allowed): ++ for alg, values in other._allowed.items(): + if alg not in self._allowed: + continue + new[alg] = [v for v in values if v in self._allowed[alg]] +@@ -89,7 +82,7 @@ def check_against_chunks(self, chunks): + + """ + gots = {} +- for hash_name in iterkeys(self._allowed): ++ for hash_name in self._allowed.keys(): + try: + gots[hash_name] = hashlib.new(hash_name) + except (ValueError, TypeError): +@@ -98,10 +91,10 @@ def check_against_chunks(self, chunks): + ) + + for chunk in chunks: +- for hash in itervalues(gots): ++ for hash in gots.values(): + hash.update(chunk) + +- for hash_name, got in iteritems(gots): ++ for hash_name, got in gots.items(): + if got.hexdigest() in self._allowed[hash_name]: + return + self._raise(gots) +diff --git a/src/pip/_internal/utils/logging.py b/src/pip/_internal/utils/logging.py +index 9a017cf7e3..44c9beabd2 100644 +--- a/src/pip/_internal/utils/logging.py ++++ b/src/pip/_internal/utils/logging.py +@@ -11,8 +11,6 @@ + import sys + from logging import Filter, getLogger + +-from pip._vendor.six import PY2 +- + from pip._internal.utils.compat import WINDOWS + from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX + from pip._internal.utils.misc import ensure_dir +@@ -62,30 +60,18 @@ class BrokenStdoutLoggingError(Exception): + pass + + +-# BrokenPipeError does not exist in Python 2 and, in addition, manifests +-# differently in Windows and non-Windows. ++# BrokenPipeError manifests differently in Windows and non-Windows. + if WINDOWS: + # In Windows, a broken pipe can show up as EINVAL rather than EPIPE: + # https://bugs.python.org/issue19612 + # https://bugs.python.org/issue30418 +- if PY2: +- def _is_broken_pipe_error(exc_class, exc): +- """See the docstring for non-Windows Python 3 below.""" +- return (exc_class is IOError and +- exc.errno in (errno.EINVAL, errno.EPIPE)) +- else: +- # In Windows, a broken pipe IOError became OSError in Python 3. +- def _is_broken_pipe_error(exc_class, exc): +- """See the docstring for non-Windows Python 3 below.""" +- return ((exc_class is BrokenPipeError) or # noqa: F821 +- (exc_class is OSError and +- exc.errno in (errno.EINVAL, errno.EPIPE))) +-elif PY2: + def _is_broken_pipe_error(exc_class, exc): +- """See the docstring for non-Windows Python 3 below.""" +- return (exc_class is IOError and exc.errno == errno.EPIPE) ++ """See the docstring for non-Windows below.""" ++ return ((exc_class is BrokenPipeError) or # noqa: F821 ++ (exc_class is OSError and ++ exc.errno in (errno.EINVAL, errno.EPIPE))) + else: +- # Then we are in the non-Windows Python 3 case. ++ # Then we are in the non-Windows case. + def _is_broken_pipe_error(exc_class, exc): + """ + Return whether an exception is a broken pipe error. +diff --git a/src/pip/_internal/utils/misc.py b/src/pip/_internal/utils/misc.py +index 4fb64d2672..aa428da056 100644 +--- a/src/pip/_internal/utils/misc.py ++++ b/src/pip/_internal/utils/misc.py +@@ -16,7 +16,10 @@ + import stat + import sys + from collections import deque +-from itertools import tee ++from io import StringIO ++from itertools import filterfalse, tee, zip_longest ++from urllib import parse as urllib_parse ++from urllib.parse import unquote as urllib_unquote + + from pip._vendor import pkg_resources + from pip._vendor.packaging.utils import canonicalize_name +@@ -24,26 +27,17 @@ + # NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is + # why we ignore the type on this import. + from pip._vendor.retrying import retry # type: ignore +-from pip._vendor.six import PY2, text_type +-from pip._vendor.six.moves import filter, filterfalse, input, map, zip_longest +-from pip._vendor.six.moves.urllib import parse as urllib_parse +-from pip._vendor.six.moves.urllib.parse import unquote as urllib_unquote + + from pip import __version__ + from pip._internal.exceptions import CommandError + from pip._internal.locations import get_major_minor_version, site_packages, user_site +-from pip._internal.utils.compat import WINDOWS, expanduser, stdlib_pkgs, str_to_display ++from pip._internal.utils.compat import WINDOWS, expanduser, stdlib_pkgs + from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast + from pip._internal.utils.virtualenv import ( + running_under_virtualenv, + virtualenv_no_global, + ) + +-if PY2: +- from io import BytesIO as StringIO +-else: +- from io import StringIO +- + if MYPY_CHECK_RUNNING: + from typing import ( + Any, +@@ -173,7 +167,7 @@ def path_to_display(path): + """ + if path is None: + return None +- if isinstance(path, text_type): ++ if isinstance(path, str): + return path + # Otherwise, path is a bytes object (str in Python 2). + try: +@@ -181,17 +175,9 @@ def path_to_display(path): + except UnicodeDecodeError: + # Include the full bytes to make troubleshooting easier, even though + # it may not be very human readable. +- if PY2: +- # Convert the bytes to a readable str representation using +- # repr(), and then convert the str to unicode. +- # Also, we add the prefix "b" to the repr() return value both +- # to make the Python 2 output look like the Python 3 output, and +- # to signal to the user that this is a bytes representation. +- display_path = str_to_display('b{!r}'.format(path)) +- else: +- # Silence the "F821 undefined name 'ascii'" flake8 error since +- # in Python 3 ascii() is a built-in. +- display_path = ascii(path) # noqa: F821 ++ # Silence the "F821 undefined name 'ascii'" flake8 error since ++ # ascii() is a built-in. ++ display_path = ascii(path) # noqa: F821 + + return display_path + +@@ -201,9 +187,6 @@ def display_path(path): + """Gives the display value for a given path, making it relative to cwd + if possible.""" + path = os.path.normcase(os.path.abspath(path)) +- if sys.version_info[0] == 2: +- path = path.decode(sys.getfilesystemencoding(), 'replace') +- path = path.encode(sys.getdefaultencoding(), 'replace') + if path.startswith(os.getcwd() + os.path.sep): + path = '.' + path[len(os.getcwd()):] + return path +@@ -854,12 +837,6 @@ def __eq__(self, other): + # just the raw, original string. + return (self.secret == other.secret) + +- # We need to provide an explicit __ne__ implementation for Python 2. +- # TODO: remove this when we drop PY2 support. +- def __ne__(self, other): +- # type: (Any) -> bool +- return not self == other +- + + def hide_value(value): + # type: (str) -> HiddenText +diff --git a/src/pip/_internal/utils/parallel.py b/src/pip/_internal/utils/parallel.py +index d4113bdc28..57082367e1 100644 +--- a/src/pip/_internal/utils/parallel.py ++++ b/src/pip/_internal/utils/parallel.py +@@ -23,8 +23,6 @@ + from multiprocessing.dummy import Pool as ThreadPool + + from pip._vendor.requests.adapters import DEFAULT_POOLSIZE +-from pip._vendor.six import PY2 +-from pip._vendor.six.moves import map + + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +@@ -100,7 +98,7 @@ def _map_multithread(func, iterable, chunksize=1): + return pool.imap_unordered(func, iterable, chunksize) + + +-if LACK_SEM_OPEN or PY2: ++if LACK_SEM_OPEN: + map_multiprocess = map_multithread = _map_fallback + else: + map_multiprocess = _map_multiprocess +diff --git a/src/pip/_internal/utils/subprocess.py b/src/pip/_internal/utils/subprocess.py +index 605e711e60..85b92c4792 100644 +--- a/src/pip/_internal/utils/subprocess.py ++++ b/src/pip/_internal/utils/subprocess.py +@@ -2,10 +2,9 @@ + + import logging + import os ++import shlex + import subprocess + +-from pip._vendor.six.moves import shlex_quote +- + from pip._internal.cli.spinners import SpinnerInterface, open_spinner + from pip._internal.exceptions import InstallationError + from pip._internal.utils.compat import console_to_str, str_to_display +@@ -51,8 +50,8 @@ def format_command_args(args): + # has type unicode and includes a non-ascii character. (The type + # checker doesn't ensure the annotations are correct in all cases.) + return ' '.join( +- shlex_quote(str(arg)) if isinstance(arg, HiddenText) +- else shlex_quote(arg) for arg in args ++ shlex.quote(str(arg)) if isinstance(arg, HiddenText) ++ else shlex.quote(arg) for arg in args + ) + + +diff --git a/src/pip/_internal/utils/urls.py b/src/pip/_internal/utils/urls.py +index f37bc8f90b..91df4c30d8 100644 +--- a/src/pip/_internal/utils/urls.py ++++ b/src/pip/_internal/utils/urls.py +@@ -1,8 +1,7 @@ + import os + import sys +- +-from pip._vendor.six.moves.urllib import parse as urllib_parse +-from pip._vendor.six.moves.urllib import request as urllib_request ++from urllib import parse as urllib_parse ++from urllib import request as urllib_request + + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +diff --git a/src/pip/_internal/utils/wheel.py b/src/pip/_internal/utils/wheel.py +index 9ce371c76e..6be61371e5 100644 +--- a/src/pip/_internal/utils/wheel.py ++++ b/src/pip/_internal/utils/wheel.py +@@ -5,11 +5,11 @@ + + import logging + from email.parser import Parser +-from zipfile import ZipFile ++from zipfile import BadZipFile, ZipFile + + from pip._vendor.packaging.utils import canonicalize_name + from pip._vendor.pkg_resources import DistInfoDistribution +-from pip._vendor.six import PY2, ensure_str ++from pip._vendor.six import ensure_str + + from pip._internal.exceptions import UnsupportedWheel + from pip._internal.utils.pkg_resources import DictMetadata +@@ -21,11 +21,6 @@ + + from pip._vendor.pkg_resources import Distribution + +-if PY2: +- from zipfile import BadZipfile as BadZipFile +-else: +- from zipfile import BadZipFile +- + + VERSION_COMPATIBLE = (1, 0) + +diff --git a/src/pip/_internal/vcs/bazaar.py b/src/pip/_internal/vcs/bazaar.py +index 3180713f7d..3a269a6477 100644 +--- a/src/pip/_internal/vcs/bazaar.py ++++ b/src/pip/_internal/vcs/bazaar.py +@@ -5,8 +5,7 @@ + + import logging + import os +- +-from pip._vendor.six.moves.urllib import parse as urllib_parse ++from urllib import parse as urllib_parse + + from pip._internal.utils.misc import display_path, rmtree + from pip._internal.utils.subprocess import make_command +diff --git a/src/pip/_internal/vcs/git.py b/src/pip/_internal/vcs/git.py +index 1831aede58..98dc3046e5 100644 +--- a/src/pip/_internal/vcs/git.py ++++ b/src/pip/_internal/vcs/git.py +@@ -6,10 +6,10 @@ + import logging + import os.path + import re ++from urllib import parse as urllib_parse ++from urllib import request as urllib_request + + from pip._vendor.packaging.version import parse as parse_version +-from pip._vendor.six.moves.urllib import parse as urllib_parse +-from pip._vendor.six.moves.urllib import request as urllib_request + + from pip._internal.exceptions import BadCommand, SubProcessError + from pip._internal.utils.misc import display_path, hide_url +diff --git a/src/pip/_internal/vcs/mercurial.py b/src/pip/_internal/vcs/mercurial.py +index 69763feaea..34a045f4c6 100644 +--- a/src/pip/_internal/vcs/mercurial.py ++++ b/src/pip/_internal/vcs/mercurial.py +@@ -3,11 +3,10 @@ + + from __future__ import absolute_import + ++import configparser + import logging + import os + +-from pip._vendor.six.moves import configparser +- + from pip._internal.exceptions import BadCommand, SubProcessError + from pip._internal.utils.misc import display_path + from pip._internal.utils.subprocess import make_command +diff --git a/src/pip/_internal/vcs/versioncontrol.py b/src/pip/_internal/vcs/versioncontrol.py +index 6724dcc697..61bf8ce34c 100644 +--- a/src/pip/_internal/vcs/versioncontrol.py ++++ b/src/pip/_internal/vcs/versioncontrol.py +@@ -8,12 +8,12 @@ + import shutil + import subprocess + import sys ++from urllib import parse as urllib_parse + + from pip._vendor import pkg_resources +-from pip._vendor.six.moves.urllib import parse as urllib_parse + + from pip._internal.exceptions import BadCommand, InstallationError, SubProcessError +-from pip._internal.utils.compat import console_to_str, samefile ++from pip._internal.utils.compat import console_to_str + from pip._internal.utils.logging import subprocess_logger + from pip._internal.utils.misc import ( + ask_path_exists, +@@ -197,7 +197,7 @@ def find_path_to_setup_from_repo_root(location, repo_root): + ) + return None + +- if samefile(repo_root, location): ++ if os.path.samefile(repo_root, location): + return None + + return os.path.relpath(location, repo_root) +@@ -289,9 +289,7 @@ def __init__(self): + # Register more schemes with urlparse for various version control + # systems + urllib_parse.uses_netloc.extend(self.schemes) +- # Python >= 2.7.4, 3.3 doesn't have uses_fragment +- if getattr(urllib_parse, 'uses_fragment', None): +- urllib_parse.uses_fragment.extend(self.schemes) ++ urllib_parse.uses_fragment.extend(self.schemes) + super(VcsSupport, self).__init__() + + def __iter__(self): +diff --git a/tests/conftest.py b/tests/conftest.py +index 78be52788a..499c121bca 100644 +--- a/tests/conftest.py ++++ b/tests/conftest.py +@@ -10,7 +10,6 @@ + from contextlib import contextmanager + + import pytest +-import six + from mock import patch + from pip._vendor.contextlib2 import ExitStack, nullcontext + from setuptools.wheel import Wheel +@@ -73,15 +72,14 @@ def pytest_collection_modifyitems(config, items): + if item.get_closest_marker('network') is not None: + item.add_marker(pytest.mark.flaky(reruns=3, reruns_delay=2)) + +- if six.PY3: +- if (item.get_closest_marker('incompatible_with_test_venv') and +- config.getoption("--use-venv")): +- item.add_marker(pytest.mark.skip( +- 'Incompatible with test venv')) +- if (item.get_closest_marker('incompatible_with_venv') and +- sys.prefix != sys.base_prefix): +- item.add_marker(pytest.mark.skip( +- 'Incompatible with venv')) ++ if (item.get_closest_marker('incompatible_with_test_venv') and ++ config.getoption("--use-venv")): ++ item.add_marker(pytest.mark.skip( ++ 'Incompatible with test venv')) ++ if (item.get_closest_marker('incompatible_with_venv') and ++ sys.prefix != sys.base_prefix): ++ item.add_marker(pytest.mark.skip( ++ 'Incompatible with venv')) + + module_path = os.path.relpath( + item.module.__file__, +@@ -111,16 +109,10 @@ def resolver_variant(request): + features = set(os.environ.get("PIP_USE_FEATURE", "").split()) + deprecated_features = set(os.environ.get("PIP_USE_DEPRECATED", "").split()) + +- if six.PY3: +- if resolver == "legacy": +- deprecated_features.add("legacy-resolver") +- else: +- deprecated_features.discard("legacy-resolver") ++ if resolver == "legacy": ++ deprecated_features.add("legacy-resolver") + else: +- if resolver == "2020-resolver": +- features.add("2020-resolver") +- else: +- features.discard("2020-resolver") ++ deprecated_features.discard("legacy-resolver") + + env = { + "PIP_USE_FEATURE": " ".join(features), +@@ -141,7 +133,7 @@ def tmpdir_factory(request, tmpdir_factory): + # handle non-ASCII file names. This works around the problem by + # passing a unicode object to rmtree(). + shutil.rmtree( +- six.text_type(tmpdir_factory.getbasetemp()), ++ str(tmpdir_factory.getbasetemp()), + ignore_errors=True, + ) + +@@ -166,7 +158,7 @@ def tmpdir(request, tmpdir): + # py.path.remove() uses str paths on Python 2 and cannot + # handle non-ASCII file names. This works around the problem by + # passing a unicode object to rmtree(). +- shutil.rmtree(six.text_type(tmpdir), ignore_errors=True) ++ shutil.rmtree(str(tmpdir), ignore_errors=True) + + + @pytest.fixture(autouse=True) +@@ -337,7 +329,7 @@ def install_egg_link(venv, project_name, egg_info_dir): + def virtualenv_template(request, tmpdir_factory, pip_src, + setuptools_install, coverage_install): + +- if six.PY3 and request.config.getoption('--use-venv'): ++ if request.config.getoption('--use-venv'): + venv_type = 'venv' + else: + venv_type = 'virtualenv' +@@ -474,10 +466,7 @@ def __init__(self, returncode, stdout): + class InMemoryPip(object): + def pip(self, *args): + orig_stdout = sys.stdout +- if six.PY3: +- stdout = io.StringIO() +- else: +- stdout = io.BytesIO() ++ stdout = io.StringIO() + sys.stdout = stdout + try: + returncode = pip_entry_point(list(args)) +diff --git a/tests/functional/test_download.py b/tests/functional/test_download.py +index 24bc4ddbcc..72b55fda92 100644 +--- a/tests/functional/test_download.py ++++ b/tests/functional/test_download.py +@@ -4,7 +4,6 @@ + from hashlib import sha256 + + import pytest +-from pip._vendor.six import PY2 + + from pip._internal.cli.status_codes import ERROR + from pip._internal.utils.urls import path_to_url +@@ -490,7 +489,6 @@ def make_wheel_with_python_requires(script, package_name, python_requires): + package_dir.joinpath('setup.py').write_text(text) + script.run( + 'python', 'setup.py', 'bdist_wheel', '--universal', cwd=package_dir, +- allow_stderr_warning=PY2, + ) + + file_name = '{}-1.0-py2.py3-none-any.whl'.format(package_name) +diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py +index f9a807bca7..aedd691a4e 100644 +--- a/tests/functional/test_install.py ++++ b/tests/functional/test_install.py +@@ -9,7 +9,6 @@ + from os.path import curdir, join, pardir + + import pytest +-from pip._vendor.six import PY2 + + from pip._internal.cli.status_codes import ERROR, SUCCESS + from pip._internal.models.index import PyPI, TestPyPI +@@ -26,8 +25,6 @@ + pyversion, + pyversion_tuple, + requirements_file, +- skip_if_not_python2, +- skip_if_python2, + windows_workaround_7667, + ) + from tests.lib.filesystem import make_socket_file +@@ -658,22 +655,7 @@ def test_editable_install__local_dir_no_setup_py_with_pyproject( + assert 'A "pyproject.toml" file was found' in msg + + +-@skip_if_not_python2 +-@pytest.mark.xfail +-def test_install_argparse_shadowed(script): +- # When argparse is in the stdlib, we support installing it +- # even though that's pretty useless because older packages did need to +- # depend on it, and not having its metadata will cause pkg_resources +- # requirements checks to fail // trigger easy-install, both of which are +- # bad. +- # XXX: Note, this test hits the outside-environment check, not the +- # in-stdlib check, because our tests run in virtualenvs... +- result = script.pip('install', 'argparse>=1.4') +- assert "Not uninstalling argparse" in result.stdout +- +- + @pytest.mark.network +-@skip_if_python2 + def test_upgrade_argparse_shadowed(script): + # If argparse is installed - even if shadowed for imported - we support + # upgrading it and properly remove the older versions files. +@@ -1568,7 +1550,7 @@ def test_install_incompatible_python_requires_wheel(script, with_wheel): + """)) + script.run( + 'python', 'setup.py', 'bdist_wheel', '--universal', +- cwd=pkga_path, allow_stderr_warning=PY2, ++ cwd=pkga_path, + ) + result = script.pip('install', './pkga/dist/pkga-0.1-py2.py3-none-any.whl', + expect_error=True) +@@ -1837,7 +1819,6 @@ def test_install_yanked_file_and_print_warning(script, data): + assert 'Successfully installed simple-3.0\n' in result.stdout, str(result) + + +-@skip_if_python2 + @pytest.mark.parametrize("install_args", [ + (), + ("--trusted-host", "localhost"), +diff --git a/tests/functional/test_install_config.py b/tests/functional/test_install_config.py +index 783f6ac7e9..dcc9c66d5a 100644 +--- a/tests/functional/test_install_config.py ++++ b/tests/functional/test_install_config.py +@@ -5,7 +5,6 @@ + + import pytest + +-from tests.lib import skip_if_python2 + from tests.lib.server import ( + authorization_response, + file_response, +@@ -130,7 +129,6 @@ def test_command_line_appends_correctly(script, data): + ), 'stdout: {}'.format(result.stdout) + + +-@skip_if_python2 + def test_config_file_override_stack( + script, virtualenv, mock_server, shared_data + ): +@@ -249,7 +247,6 @@ def test_prompt_for_authentication(script, data, cert_factory): + result.stdout, str(result) + + +-@skip_if_python2 + def test_do_not_prompt_for_authentication(script, data, cert_factory): + """Test behaviour if --no-input option is given while installing + from a index url requiring authentication +diff --git a/tests/functional/test_install_index.py b/tests/functional/test_install_index.py +index e887595b93..8e432b9540 100644 +--- a/tests/functional/test_install_index.py ++++ b/tests/functional/test_install_index.py +@@ -1,7 +1,6 @@ + import os + import textwrap +- +-from pip._vendor.six.moves.urllib import parse as urllib_parse ++from urllib import parse as urllib_parse + + + def test_find_links_relative_path(script, data, with_wheel): +diff --git a/tests/functional/test_install_wheel.py b/tests/functional/test_install_wheel.py +index ad4e749676..177e86db32 100644 +--- a/tests/functional/test_install_wheel.py ++++ b/tests/functional/test_install_wheel.py +@@ -8,7 +8,7 @@ + + import pytest + +-from tests.lib import create_basic_wheel_for_package, skip_if_python2 ++from tests.lib import create_basic_wheel_for_package + from tests.lib.path import Path + from tests.lib.wheel import make_wheel + +@@ -118,7 +118,6 @@ def test_basic_install_from_wheel_file(script, data): + + # Installation seems to work, but scripttest fails to check. + # I really don't care now since we're desupporting it soon anyway. +-@skip_if_python2 + def test_basic_install_from_unicode_wheel(script, data): + """ + Test installing from a wheel (that has a script) +@@ -394,8 +393,6 @@ def test_install_from_wheel_gen_uppercase_entrypoint( + assert bool(os.access(script.base_path / wrapper_file, os.X_OK)) + + +-# pkg_resources.EntryPoint() does not parse unicode correctly on Python 2. +-@skip_if_python2 + def test_install_from_wheel_gen_unicode_entrypoint(script): + make_wheel( + "script_wheel_unicode", +@@ -651,8 +648,6 @@ def test_wheel_installs_ok_with_badly_encoded_irrelevant_dist_info_file( + ) + + +-# Metadata is not decoded on Python 2. +-@skip_if_python2 + def test_wheel_install_fails_with_badly_encoded_metadata(script): + package = create_basic_wheel_for_package( + script, +diff --git a/tests/functional/test_warning.py b/tests/functional/test_warning.py +index ff228421e6..3558704bce 100644 +--- a/tests/functional/test_warning.py ++++ b/tests/functional/test_warning.py +@@ -1,10 +1,7 @@ +-import platform + import textwrap + + import pytest + +-from tests.lib import skip_if_not_python2, skip_if_python2 +- + + @pytest.fixture + def warnings_demo(tmpdir): +@@ -37,33 +34,11 @@ def test_deprecation_warnings_can_be_silenced(script, warnings_demo): + CPYTHON_DEPRECATION_TEXT = "January 1st, 2020" + + +-@skip_if_python2 + def test_version_warning_is_not_shown_if_python_version_is_not_2(script): + result = script.pip("debug", allow_stderr_warning=True) + assert DEPRECATION_TEXT not in result.stderr, str(result) + assert CPYTHON_DEPRECATION_TEXT not in result.stderr, str(result) + + +-@skip_if_python2 + def test_flag_does_nothing_if_python_version_is_not_2(script): + script.pip("list", "--no-python-version-warning") +- +- +-@skip_if_not_python2 +-def test_version_warning_is_shown_if_python_version_is_2(script): +- result = script.pip("debug", allow_stderr_warning=True) +- assert DEPRECATION_TEXT in result.stderr, str(result) +- if platform.python_implementation() == 'CPython': +- assert CPYTHON_DEPRECATION_TEXT in result.stderr, str(result) +- else: +- assert CPYTHON_DEPRECATION_TEXT not in result.stderr, str(result) +- +- +-@skip_if_not_python2 +-def test_version_warning_is_not_shown_when_flag_is_passed(script): +- result = script.pip( +- "debug", "--no-python-version-warning", allow_stderr_warning=True +- ) +- assert DEPRECATION_TEXT not in result.stderr, str(result) +- assert CPYTHON_DEPRECATION_TEXT not in result.stderr, str(result) +- assert "--no-python-version-warning" not in result.stderr +diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py +index 07569d814f..b7c63029a4 100644 +--- a/tests/lib/__init__.py ++++ b/tests/lib/__init__.py +@@ -15,7 +15,7 @@ + from zipfile import ZipFile + + import pytest +-from pip._vendor.six import PY2, ensure_binary, text_type ++from pip._vendor.six import ensure_binary + from scripttest import FoundDir, TestFileEnvironment + + from pip._internal.index.collector import LinkCollector +@@ -1099,15 +1099,12 @@ def create_basic_sdist_for_package( + path.parent.mkdir(exist_ok=True, parents=True) + path.write_bytes(ensure_binary(files[fname])) + +- # The base_dir cast is required to make `shutil.make_archive()` use +- # Unicode paths on Python 2, making it able to properly archive +- # files with non-ASCII names. + retval = script.scratch_path / archive_name + generated = shutil.make_archive( + retval, + 'gztar', + root_dir=script.temp_path, +- base_dir=text_type(os.curdir), ++ base_dir=os.curdir, + ) + shutil.move(generated, retval) + +@@ -1164,10 +1161,6 @@ def need_mercurial(fn): + )(fn)) + + +-skip_if_python2 = pytest.mark.skipif(PY2, reason="Non-Python 2 only") +-skip_if_not_python2 = pytest.mark.skipif(not PY2, reason="Python 2 only") +- +- + # Workaround for test failures after new wheel release. + windows_workaround_7667 = pytest.mark.skipif( + "sys.platform == 'win32' and sys.version_info < (3,)", +diff --git a/tests/lib/local_repos.py b/tests/lib/local_repos.py +index 2a41595f9f..6899677eed 100644 +--- a/tests/lib/local_repos.py ++++ b/tests/lib/local_repos.py +@@ -2,8 +2,7 @@ + + import os + import subprocess +- +-from pip._vendor.six.moves.urllib import request as urllib_request ++from urllib import request as urllib_request + + from pip._internal.utils.misc import hide_url + from pip._internal.utils.typing import MYPY_CHECK_RUNNING +diff --git a/tests/lib/path.py b/tests/lib/path.py +index d1ea6bc5e8..ec4f1e37c5 100644 +--- a/tests/lib/path.py ++++ b/tests/lib/path.py +@@ -5,9 +5,6 @@ + + import glob + import os +-import shutil +- +-from pip._vendor import six + + try: + from os import supports_fd +@@ -15,11 +12,7 @@ + supports_fd = set() + + +- +-_base = six.text_type if os.path.supports_unicode_filenames else str +- +- +-class Path(_base): ++class Path(str): + """ + Models a path in an object oriented way. + """ +@@ -32,8 +25,8 @@ class Path(_base): + + def __new__(cls, *paths): + if len(paths): +- return _base.__new__(cls, os.path.join(*paths)) +- return _base.__new__(cls) ++ return str.__new__(cls, os.path.join(*paths)) ++ return str.__new__(cls) + + def __div__(self, path): + """ +@@ -71,20 +64,20 @@ def __add__(self, path): + >>> Path('/home/a') + 'bc.d' + '/home/abc.d' + """ +- return Path(_base(self) + path) ++ return Path(str(self) + path) + + def __radd__(self, path): + """ + >>> '/home/a' + Path('bc.d') + '/home/abc.d' + """ +- return Path(path + _base(self)) ++ return Path(path + str(self)) + + def __repr__(self): +- return u"Path({inner})".format(inner=_base.__repr__(self)) ++ return u"Path({inner})".format(inner=str.__repr__(self)) + + def __hash__(self): +- return _base.__hash__(self) ++ return str.__hash__(self) + + @property + def name(self): +diff --git a/tests/lib/test_wheel.py b/tests/lib/test_wheel.py +index a6f46cd899..15e5a75fe1 100644 +--- a/tests/lib/test_wheel.py ++++ b/tests/lib/test_wheel.py +@@ -5,7 +5,7 @@ + from functools import partial + from zipfile import ZipFile + +-from pip._vendor.six import ensure_text, iteritems ++from pip._vendor.six import ensure_text + + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + from tests.lib.wheel import ( +@@ -182,7 +182,7 @@ def test_make_wheel_default_record(): + ], + "simple-0.1.0.dist-info/RECORD": ["", ""], + } +- for name, values in iteritems(expected): ++ for name, values in expected.items(): + assert records[name] == values, name + + # WHEEL and METADATA aren't constructed in a stable way, so just spot +@@ -191,7 +191,7 @@ def test_make_wheel_default_record(): + "simple-0.1.0.dist-info/METADATA": "51", + "simple-0.1.0.dist-info/WHEEL": "104", + } +- for name, length in iteritems(expected_variable): ++ for name, length in expected_variable.items(): + assert records[name][0].startswith("sha256="), name + assert records[name][1] == length, name + +diff --git a/tests/lib/venv.py b/tests/lib/venv.py +index cc94e29f25..045dd78a76 100644 +--- a/tests/lib/venv.py ++++ b/tests/lib/venv.py +@@ -4,15 +4,12 @@ + import shutil + import sys + import textwrap ++import venv as _venv + +-import six + import virtualenv as _virtualenv + + from .path import Path + +-if six.PY3: +- import venv as _venv +- + + class VirtualEnvironment(object): + """ +@@ -37,8 +34,7 @@ def _update_paths(self): + self.site = Path(lib) / 'site-packages' + # Workaround for https://github.com/pypa/virtualenv/issues/306 + if hasattr(sys, "pypy_version_info"): +- version_fmt = '{0}' if six.PY3 else '{0}.{1}' +- version_dir = version_fmt.format(*sys.version_info) ++ version_dir = '{0}'.format(*sys.version_info) + self.lib = Path(home, 'lib-python', version_dir) + else: + self.lib = Path(lib) +diff --git a/tests/lib/wheel.py b/tests/lib/wheel.py +index d89a680a19..b5e222fda4 100644 +--- a/tests/lib/wheel.py ++++ b/tests/lib/wheel.py +@@ -13,7 +13,7 @@ + + import csv23 + from pip._vendor.requests.structures import CaseInsensitiveDict +-from pip._vendor.six import ensure_binary, ensure_text, iteritems ++from pip._vendor.six import ensure_binary, ensure_text + + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + from tests.lib.path import Path +@@ -68,7 +68,7 @@ def message_from_dict(headers): + List values are converted into repeated headers in the result. + """ + message = Message() +- for name, value in iteritems(headers): ++ for name, value in headers.items(): + if isinstance(value, list): + for v in value: + message[name] = v +@@ -161,7 +161,7 @@ def make_entry_points_file( + entry_points_data["console_scripts"] = console_scripts + + lines = [] +- for section, values in iteritems(entry_points_data): ++ for section, values in entry_points_data.items(): + lines.append("[{}]".format(section)) + lines.extend(values) + +@@ -175,7 +175,7 @@ def make_files(files): + # type: (Dict[str, AnyStr]) -> List[File] + return [ + File(name, ensure_binary(contents)) +- for name, contents in iteritems(files) ++ for name, contents in files.items() + ] + + +@@ -184,7 +184,7 @@ def make_metadata_files(name, version, files): + get_path = partial(dist_info_path, name, version) + return [ + File(get_path(name), ensure_binary(contents)) +- for name, contents in iteritems(files) ++ for name, contents in files.items() + ] + + +@@ -193,7 +193,7 @@ def make_data_files(name, version, files): + data_dir = "{}-{}.data".format(name, version) + return [ + File("{}/{}".format(data_dir, name), ensure_binary(contents)) +- for name, contents in iteritems(files) ++ for name, contents in files.items() + ] + + +diff --git a/tests/unit/test_collector.py b/tests/unit/test_collector.py +index fa1057b640..4384812fc6 100644 +--- a/tests/unit/test_collector.py ++++ b/tests/unit/test_collector.py +@@ -3,13 +3,13 @@ + import re + import uuid + from textwrap import dedent ++from urllib import request as urllib_request + + import mock + import pretend + import pytest + from mock import Mock, patch + from pip._vendor import html5lib, requests +-from pip._vendor.six.moves.urllib import request as urllib_request + + from pip._internal.exceptions import NetworkConnectionError + from pip._internal.index.collector import ( +@@ -30,7 +30,7 @@ + from pip._internal.models.index import PyPI + from pip._internal.models.link import Link + from pip._internal.network.session import PipSession +-from tests.lib import make_test_link_collector, skip_if_python2 ++from tests.lib import make_test_link_collector + + + @pytest.mark.parametrize( +@@ -406,7 +406,6 @@ def test_parse_links__yanked_reason(anchor_html, expected): + assert actual == expected + + +-@skip_if_python2 + def test_parse_links_caches_same_page_by_url(): + html = ( + '
' +diff --git a/tests/unit/test_logging.py b/tests/unit/test_logging.py +index 10d47eb614..54bce7052a 100644 +--- a/tests/unit/test_logging.py ++++ b/tests/unit/test_logging.py +@@ -1,10 +1,8 @@ +-import errno + import logging + from threading import Thread + + import pytest + from mock import patch +-from pip._vendor.six import PY2 + + from pip._internal.utils.logging import ( + BrokenStdoutLoggingError, +@@ -17,19 +15,6 @@ + logger = logging.getLogger(__name__) + + +-# This is a Python 2/3 compatibility helper. +-def _make_broken_pipe_error(): +- """ +- Return an exception object representing a broken pipe. +- """ +- if PY2: +- # This is one way a broken pipe error can show up in Python 2 +- # (a non-Windows example in this case). +- return IOError(errno.EPIPE, 'Broken pipe') +- +- return BrokenPipeError() # noqa: F821 +- +- + class TestIndentingFormatter(object): + """Test ``pip._internal.utils.logging.IndentingFormatter``.""" + +@@ -146,7 +131,7 @@ def test_broken_pipe_in_stderr_flush(self): + with captured_stderr() as stderr: + handler = ColorizedStreamHandler(stream=stderr) + with patch('sys.stderr.flush') as mock_flush: +- mock_flush.side_effect = _make_broken_pipe_error() ++ mock_flush.side_effect = BrokenPipeError() + # The emit() call raises no exception. + handler.emit(record) + +@@ -154,13 +139,9 @@ def test_broken_pipe_in_stderr_flush(self): + + assert err_text.startswith('my error') + # Check that the logging framework tried to log the exception. +- if PY2: +- assert 'IOError: [Errno 32] Broken pipe' in err_text +- assert 'Logged from file' in err_text +- else: +- assert 'Logging error' in err_text +- assert 'BrokenPipeError' in err_text +- assert "Message: 'my error'" in err_text ++ assert 'Logging error' in err_text ++ assert 'BrokenPipeError' in err_text ++ assert "Message: 'my error'" in err_text + + def test_broken_pipe_in_stdout_write(self): + """ +@@ -173,7 +154,7 @@ def test_broken_pipe_in_stdout_write(self): + with captured_stdout() as stdout: + handler = ColorizedStreamHandler(stream=stdout) + with patch('sys.stdout.write') as mock_write: +- mock_write.side_effect = _make_broken_pipe_error() ++ mock_write.side_effect = BrokenPipeError() + with pytest.raises(BrokenStdoutLoggingError): + handler.emit(record) + +@@ -188,7 +169,7 @@ def test_broken_pipe_in_stdout_flush(self): + with captured_stdout() as stdout: + handler = ColorizedStreamHandler(stream=stdout) + with patch('sys.stdout.flush') as mock_flush: +- mock_flush.side_effect = _make_broken_pipe_error() ++ mock_flush.side_effect = BrokenPipeError() + with pytest.raises(BrokenStdoutLoggingError): + handler.emit(record) + +diff --git a/tests/unit/test_req_file.py b/tests/unit/test_req_file.py +index f995d05a67..4812637ee5 100644 +--- a/tests/unit/test_req_file.py ++++ b/tests/unit/test_req_file.py +@@ -6,7 +6,6 @@ + + import pytest + from mock import patch +-from pip._vendor.six import PY2 + from pretend import stub + + import pip._internal.req.req_file # this will be monkeypatched +@@ -220,12 +219,11 @@ def test_error_message(self, line_processor): + line_number=3 + ) + +- package_name = "u'my-package=1.0'" if PY2 else "'my-package=1.0'" + expected = ( +- "Invalid requirement: {} " ++ "Invalid requirement: 'my-package=1.0' " + '(from line 3 of path/requirements.txt)\n' + 'Hint: = is not a valid operator. Did you mean == ?' +- ).format(package_name) ++ ) + assert str(exc.value) == expected + + def test_yield_line_requirement(self, line_processor): +diff --git a/tests/unit/test_urls.py b/tests/unit/test_urls.py +index 7428cef9eb..9c6f75a802 100644 +--- a/tests/unit/test_urls.py ++++ b/tests/unit/test_urls.py +@@ -1,8 +1,8 @@ + import os + import sys ++from urllib import request as urllib_request + + import pytest +-from pip._vendor.six.moves.urllib import request as urllib_request + + from pip._internal.utils.urls import get_url_scheme, path_to_url, url_to_path + +diff --git a/tests/unit/test_utils_parallel.py b/tests/unit/test_utils_parallel.py +index 6086dcaa08..d5449988e3 100644 +--- a/tests/unit/test_utils_parallel.py ++++ b/tests/unit/test_utils_parallel.py +@@ -5,11 +5,9 @@ + from math import factorial + from sys import modules + +-from pip._vendor.six import PY2 +-from pip._vendor.six.moves import map + from pytest import mark + +-DUNDER_IMPORT = '__builtin__.__import__' if PY2 else 'builtins.__import__' ++DUNDER_IMPORT = 'builtins.__import__' + FUNC, ITERABLE = factorial, range(42) + MAPS = 'map_multiprocess', 'map_multithread' + _import = __import__ +@@ -63,9 +61,8 @@ def test_lack_sem_open(name, monkeypatch): + def test_have_sem_open(name, monkeypatch): + """Test fallback when sem_open is available.""" + monkeypatch.setattr(DUNDER_IMPORT, have_sem_open) +- impl = '_map_fallback' if PY2 else '_{}'.format(name) + with tmp_import_parallel() as parallel: +- assert getattr(parallel, name) is getattr(parallel, impl) ++ assert getattr(parallel, name) is getattr(parallel, '_{}'.format(name)) + + + @mark.parametrize('name', MAPS) +diff --git a/tests/unit/test_utils_pkg_resources.py b/tests/unit/test_utils_pkg_resources.py +index d113d6df12..ae7357ba1c 100644 +--- a/tests/unit/test_utils_pkg_resources.py ++++ b/tests/unit/test_utils_pkg_resources.py +@@ -6,7 +6,6 @@ + + from pip._internal.utils.packaging import get_metadata, get_requires_python + from pip._internal.utils.pkg_resources import DictMetadata +-from tests.lib import skip_if_python2 + + + def test_dict_metadata_works(): +@@ -45,8 +44,6 @@ def test_dict_metadata_works(): + assert requires_python == get_requires_python(dist) + + +-# Metadata is not decoded on Python 2, so no chance for error. +-@skip_if_python2 + def test_dict_metadata_throws_on_bad_unicode(): + metadata = DictMetadata({ + "METADATA": b"\xff" +diff --git a/tests/unit/test_utils_wheel.py b/tests/unit/test_utils_wheel.py +index cf8bd6dc3e..abd3011480 100644 +--- a/tests/unit/test_utils_wheel.py ++++ b/tests/unit/test_utils_wheel.py +@@ -9,7 +9,6 @@ + from pip._internal.exceptions import UnsupportedWheel + from pip._internal.utils import wheel + from pip._internal.utils.typing import MYPY_CHECK_RUNNING +-from tests.lib import skip_if_python2 + + if MYPY_CHECK_RUNNING: + from tests.lib.path import Path +@@ -88,7 +87,6 @@ def test_wheel_metadata_fails_missing_wheel(tmpdir, zip_dir): + assert "could not read" in str(e.value) + + +-@skip_if_python2 + def test_wheel_metadata_fails_on_bad_encoding(tmpdir, zip_dir): + dist_info_dir = tmpdir / "simple-0.1.0.dist-info" + dist_info_dir.mkdir() +diff --git a/tests/unit/test_vcs_mercurial.py b/tests/unit/test_vcs_mercurial.py +index 630619b823..07224c0a4d 100644 +--- a/tests/unit/test_vcs_mercurial.py ++++ b/tests/unit/test_vcs_mercurial.py +@@ -2,10 +2,9 @@ + Contains functional tests of the Mercurial class. + """ + ++import configparser + import os + +-from pip._vendor.six.moves import configparser +- + from pip._internal.utils.misc import hide_url + from pip._internal.vcs.mercurial import Mercurial + from tests.lib import need_mercurial +diff --git a/tests/unit/test_wheel.py b/tests/unit/test_wheel.py +index 35916058a7..a97ec89da4 100644 +--- a/tests/unit/test_wheel.py ++++ b/tests/unit/test_wheel.py +@@ -25,7 +25,7 @@ + from pip._internal.utils.misc import hash_file + from pip._internal.utils.unpacking import unpack_file + from pip._internal.utils.wheel import pkg_resources_distribution_for_wheel +-from tests.lib import DATA_DIR, assert_paths_equal, skip_if_python2 ++from tests.lib import DATA_DIR, assert_paths_equal + from tests.lib.wheel import make_wheel + + +@@ -83,7 +83,7 @@ def test_get_legacy_build_wheel_path__multiple_names(caplog): + [ + u"pip = pip._internal.main:pip", + u"pip:pip = pip._internal.main:pip", +- pytest.param(u"進入點 = 套件.模組:函式", marks=skip_if_python2), ++ u"進入點 = 套件.模組:函式", + ], + ) + def test_get_entrypoints(console_scripts): +diff --git a/tox.ini b/tox.ini +index e458e374b5..7fcc2c664a 100644 +--- a/tox.ini ++++ b/tox.ini +@@ -2,7 +2,7 @@ + minversion = 3.4.0 + envlist = + docs, packaging, lint, vendoring, +- py27, py35, py36, py37, py38, py39, pypy, pypy3 ++ py36, py37, py38, py39, pypy3 + + [helpers] + # Wrapper for calls to pip that make sure the version being used is the -- Gitee From 970e08c959dc6db48352ca2797a051632459f3d9 Mon Sep 17 00:00:00 2001 From: openeuler-ci-bot <80474298@qq.com> Date: Thu, 24 Dec 2020 01:58:54 +0800 Subject: [PATCH 2/2] [patch tracking] 20201224015849754669 - update spec file --- python-pip.spec | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/python-pip.spec b/python-pip.spec index 3d035b5..ad89e20 100644 --- a/python-pip.spec +++ b/python-pip.spec @@ -6,7 +6,7 @@ pip is the package installer for Python. You can use pip to install packages fro %global bashcompdir %(b=$(pkg-config --variable=completionsdir bash-completion 2>/dev/null); echo ${b:-%{_sysconfdir}/bash_completion.d}) Name: python-%{srcname} Version: 20.2.2 -Release: 3 +Release: 4 Summary: A tool for installing and managing Python packages License: MIT and Python and ASL 2.0 and BSD and ISC and LGPLv2 and MPLv2.0 and (ASL 2.0 or BSD) URL: http://www.pip-installer.org @@ -16,6 +16,7 @@ Patch1: allow-stripping-given-prefix-from-wheel-RECORD-files.patch Patch2: emit-a-warning-when-running-with-root-privileges.patch Patch3: remove-existing-dist-only-if-path-conflicts.patch Patch6000: dummy-certifi.patch +Patch6001: 1eebb12550cf4e207dc654b57553efe818792747.patch Source10: pip-allow-older-versions.patch %description %{_description} @@ -112,6 +113,9 @@ install -p dist/%{python_wheelname} -t %{buildroot}%{python_wheeldir} %{python_wheeldir}/%{python_wheelname} %changelog +* 20201224015849754669 patch-tracking 20.2.2-4 +- append patch file of upstream repository from <1eebb12550cf4e207dc654b57553efe818792747> to <1eebb12550cf4e207dc654b57553efe818792747> + * Wed Nov 4 2020 wangjie