diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 58a95bbfb..636fdfd3d 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 94ff37fc3..1d625613b 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 17d11e7d1..a133e57f2 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 1a5507fdc..1b5351326 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 f1c69d09c..e6eeb5ebf 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 000000000..79d8e5081 --- /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 ce93552b3..190c4d86e 100644 --- a/src/pip/_internal/cli/base_command.py +++ b/src/pip/_internal/cli/base_command.py @@ -9,8 +9,6 @@ import os 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 @@ class Command(CommandContextMixIn): 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 7170bfd38..b64e96780 100644 --- a/src/pip/_internal/cli/parser.py +++ b/src/pip/_internal/cli/parser.py @@ -7,16 +7,15 @@ from __future__ import absolute_import 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 @@ class PrettyHelpFormatter(optparse.IndentedHelpFormatter): # 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 @@ class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): 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 @@ class ConfigOptionParser(CustomOptionParser): 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 69338552f..968a8c7cf 100644 --- a/src/pip/_internal/cli/progress_bars.py +++ b/src/pip/_internal/cli/progress_bars.py @@ -4,7 +4,6 @@ import itertools 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 008066ab1..2adbb7411 100644 --- a/src/pip/_internal/cli/req_command.py +++ b/src/pip/_internal/cli/req_command.py @@ -9,8 +9,6 @@ import logging 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 @@ class RequirementCommand(IndexGroupCommand): 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 27b15d70a..3f29e48b4 100644 --- a/src/pip/_internal/commands/list.py +++ b/src/pip/_internal/commands/list.py @@ -3,8 +3,6 @@ from __future__ import absolute_import 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 146d653e5..f382ddc65 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.cli.status_codes import NO_MATCHES_FOUND, SUCCESS 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 @@ class SearchCommand(Command, SessionCommandMixin): 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 23614fd2b..9e8357855 100644 --- a/src/pip/_internal/configuration.py +++ b/src/pip/_internal/configuration.py @@ -11,13 +11,12 @@ Some terminology: 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 3a789f804..dc7ae96aa 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 @@ if MYPY_CHECK_RUNNING: 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 56482caf7..870205584 100644 --- a/src/pip/_internal/exceptions.py +++ b/src/pip/_internal/exceptions.py @@ -4,25 +4,18 @@ from __future__ import absolute_import 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 @@ class HashMismatch(HashError): 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 b850b8cbe..b852645fd 100644 --- a/src/pip/_internal/index/collector.py +++ b/src/pip/_internal/index/collector.py @@ -10,12 +10,12 @@ import mimetypes 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 99aa68d12..8f544caf6 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 5b4a1fe22..0374d7f55 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 29ef402be..4ad4f7bde 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 d732504e6..ab6f91486 100644 --- a/src/pip/_internal/models/search_scope.py +++ b/src/pip/_internal/models/search_scope.py @@ -2,9 +2,9 @@ import itertools 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 357811a16..3de21518e 100644 --- a/src/pip/_internal/network/auth.py +++ b/src/pip/_internal/network/auth.py @@ -5,10 +5,10 @@ providing credentials in the context of network requests. """ 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 608475aba..83704f6f1 100644 --- a/src/pip/_internal/network/lazy_wheel.py +++ b/src/pip/_internal/network/lazy_wheel.py @@ -8,7 +8,6 @@ from tempfile import NamedTemporaryFile 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 454945d9a..5839c4d28 100644 --- a/src/pip/_internal/network/session.py +++ b/src/pip/_internal/network/session.py @@ -13,13 +13,13 @@ import os 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 504018f28..d025a145a 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 d4f790cd4..ba885afd6 100644 --- a/src/pip/_internal/operations/freeze.py +++ b/src/pip/_internal/operations/freeze.py @@ -4,7 +4,6 @@ import collections 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 8b67ebb94..7b7d48661 100644 --- a/src/pip/_internal/operations/install/wheel.py +++ b/src/pip/_internal/operations/install/wheel.py @@ -15,14 +15,13 @@ import shutil 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 @@ else: 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 @@ logger = logging.getLogger(__name__) 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 @@ class ZipBackedFile(object): 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 _install_wheel( 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 _install_wheel( ) 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 _install_wheel( 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 _install_wheel( 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 13b2c0bee..f1321709b 100644 --- a/src/pip/_internal/operations/prepare.py +++ b/src/pip/_internal/operations/prepare.py @@ -10,7 +10,6 @@ import os 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 @@ if MYPY_CHECK_RUNNING: 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 _copy_source_tree(source, target): 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 4144a9ed6..ee90de12e 100644 --- a/src/pip/_internal/pyproject.py +++ b/src/pip/_internal/pyproject.py @@ -2,10 +2,9 @@ from __future__ import absolute_import 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 0af60fa05..ae891ce7a 100644 --- a/src/pip/_internal/req/req_file.py +++ b/src/pip/_internal/req/req_file.py @@ -8,9 +8,7 @@ import optparse 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 get_line_parser(finder): 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 866d18fcb..067affd53 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -223,7 +223,7 @@ class InstallRequirement(object): 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 @@ class InstallRequirement(object): 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 @@ class InstallRequirement(object): 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 2e7dfcc73..b70d5e7f4 100644 --- a/src/pip/_internal/req/req_uninstall.py +++ b/src/pip/_internal/req/req_uninstall.py @@ -6,12 +6,13 @@ import logging 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 @@ class UninstallPathSet(object): # __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 @@ class UninstallPthEntries(object): # 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 2196e6e0a..e6ddbcb5f 100644 --- a/src/pip/_internal/utils/compat.py +++ b/src/pip/_internal/utils/compat.py @@ -11,15 +11,12 @@ import functools 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 @@ except ImportError: __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 @@ WINDOWS = (sys.platform.startswith("win") or (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 303243fd2..1b0c083cf 100644 --- a/src/pip/_internal/utils/filesystem.py +++ b/src/pip/_internal/utils/filesystem.py @@ -12,7 +12,6 @@ from tempfile import NamedTemporaryFile # 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 4d90f5bfd..53da6636e 100644 --- a/src/pip/_internal/utils/hashes.py +++ b/src/pip/_internal/utils/hashes.py @@ -2,21 +2,14 @@ from __future__ import absolute_import 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 @@ class Hashes(object): # 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 @@ class Hashes(object): """ 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 @@ class Hashes(object): ) 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 9a017cf7e..44c9beabd 100644 --- a/src/pip/_internal/utils/logging.py +++ b/src/pip/_internal/utils/logging.py @@ -11,8 +11,6 @@ import os 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 4fb64d267..aa428da05 100644 --- a/src/pip/_internal/utils/misc.py +++ b/src/pip/_internal/utils/misc.py @@ -16,7 +16,10 @@ import shutil 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 @@ from pip._vendor.packaging.utils import canonicalize_name # 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 @@ class HiddenText(object): # 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 d4113bdc2..57082367e 100644 --- a/src/pip/_internal/utils/parallel.py +++ b/src/pip/_internal/utils/parallel.py @@ -23,8 +23,6 @@ from multiprocessing import Pool as ProcessPool 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 605e711e6..85b92c479 100644 --- a/src/pip/_internal/utils/subprocess.py +++ b/src/pip/_internal/utils/subprocess.py @@ -2,10 +2,9 @@ from __future__ import absolute_import 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 f37bc8f90..91df4c30d 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 9ce371c76..6be61371e 100644 --- a/src/pip/_internal/utils/wheel.py +++ b/src/pip/_internal/utils/wheel.py @@ -5,11 +5,11 @@ from __future__ import absolute_import 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 @@ if MYPY_CHECK_RUNNING: 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 3180713f7..3a269a647 100644 --- a/src/pip/_internal/vcs/bazaar.py +++ b/src/pip/_internal/vcs/bazaar.py @@ -5,8 +5,7 @@ from __future__ import absolute_import 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 1831aede5..98dc3046e 100644 --- a/src/pip/_internal/vcs/git.py +++ b/src/pip/_internal/vcs/git.py @@ -6,10 +6,10 @@ from __future__ import absolute_import 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 69763feae..34a045f4c 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 6724dcc69..61bf8ce34 100644 --- a/src/pip/_internal/vcs/versioncontrol.py +++ b/src/pip/_internal/vcs/versioncontrol.py @@ -8,12 +8,12 @@ import os 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 @@ class VcsSupport(object): # 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 78be52788..499c121bc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,7 +10,6 @@ import time 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 @@ class InMemoryPipResult(object): 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 24bc4ddbc..72b55fda9 100644 --- a/tests/functional/test_download.py +++ b/tests/functional/test_download.py @@ -4,7 +4,6 @@ import textwrap 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 f9a807bca..aedd691a4 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -9,7 +9,6 @@ import textwrap 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 @@ from tests.lib import ( 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 783f6ac7e..dcc9c66d5 100644 --- a/tests/functional/test_install_config.py +++ b/tests/functional/test_install_config.py @@ -5,7 +5,6 @@ import textwrap 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 e887595b9..8e432b954 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 ad4e74967..177e86db3 100644 --- a/tests/functional/test_install_wheel.py +++ b/tests/functional/test_install_wheel.py @@ -8,7 +8,7 @@ import shutil 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 ff228421e..3558704bc 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 @@ DEPRECATION_TEXT = "drop support for Python 2.7" 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 07569d814..b7c63029a 100644 --- a/tests/lib/__init__.py +++ b/tests/lib/__init__.py @@ -15,7 +15,7 @@ from textwrap import dedent 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 2a41595f9..6899677ee 100644 --- a/tests/lib/local_repos.py +++ b/tests/lib/local_repos.py @@ -2,8 +2,7 @@ from __future__ import absolute_import 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 d1ea6bc5e..ec4f1e37c 100644 --- a/tests/lib/path.py +++ b/tests/lib/path.py @@ -5,9 +5,6 @@ from __future__ import absolute_import import glob import os -import shutil - -from pip._vendor import six try: from os import supports_fd @@ -15,11 +12,7 @@ except ImportError: 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 @@ class Path(_base): >>> 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 a6f46cd89..15e5a75fe 100644 --- a/tests/lib/test_wheel.py +++ b/tests/lib/test_wheel.py @@ -5,7 +5,7 @@ from email import message_from_string 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 cc94e29f2..045dd78a7 100644 --- a/tests/lib/venv.py +++ b/tests/lib/venv.py @@ -4,15 +4,12 @@ import compileall 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 @@ class VirtualEnvironment(object): 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 d89a680a1..b5e222fda 100644 --- a/tests/lib/wheel.py +++ b/tests/lib/wheel.py @@ -13,7 +13,7 @@ from zipfile import ZipFile 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 fa1057b64..4384812fc 100644 --- a/tests/unit/test_collector.py +++ b/tests/unit/test_collector.py @@ -3,13 +3,13 @@ import os.path 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.index.collector import ( 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 10d47eb61..54bce7052 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 @@ from pip._internal.utils.misc import captured_stderr, captured_stdout 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 @@ class TestColorizedStreamHandler(object): 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 @@ class TestColorizedStreamHandler(object): 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 @@ class TestColorizedStreamHandler(object): 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 @@ class TestColorizedStreamHandler(object): 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 f995d05a6..4812637ee 100644 --- a/tests/unit/test_req_file.py +++ b/tests/unit/test_req_file.py @@ -6,7 +6,6 @@ import textwrap 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 @@ class TestProcessLine(object): 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 7428cef9e..9c6f75a80 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 6086dcaa0..d5449988e 100644 --- a/tests/unit/test_utils_parallel.py +++ b/tests/unit/test_utils_parallel.py @@ -5,11 +5,9 @@ from importlib import import_module 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 d113d6df1..ae7357ba1 100644 --- a/tests/unit/test_utils_pkg_resources.py +++ b/tests/unit/test_utils_pkg_resources.py @@ -6,7 +6,6 @@ from pip._vendor.six import ensure_binary 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 cf8bd6dc3..abd301148 100644 --- a/tests/unit/test_utils_wheel.py +++ b/tests/unit/test_utils_wheel.py @@ -9,7 +9,6 @@ from pip._vendor.contextlib2 import ExitStack 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 630619b82..07224c0a4 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 35916058a..a97ec89da 100644 --- a/tests/unit/test_wheel.py +++ b/tests/unit/test_wheel.py @@ -25,7 +25,7 @@ from pip._internal.utils.compat import WINDOWS 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 e458e374b..7fcc2c664 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