diff --git a/news/5204.bugfix.rst b/news/5204.bugfix.rst new file mode 100644 index 0000000000..a2eda77d5f --- /dev/null +++ b/news/5204.bugfix.rst @@ -0,0 +1 @@ +Remove usages of ``pip_shims`` from the non vendored ``pipenv`` code, but retain initialization for ``requirementslib`` still has usages. diff --git a/pipenv/core.py b/pipenv/core.py index 0413e481e9..9a82df6317 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -17,6 +17,12 @@ from pipenv import environments, exceptions, pep508checker, progress from pipenv._compat import decode_for_output, fix_utf8 +from pipenv.patched.pip._internal.exceptions import PipError +from pipenv.patched.pip._internal.network.session import PipSession +from pipenv.patched.pip._internal.req.constructors import ( + install_req_from_parsed_requirement, +) +from pipenv.patched.pip._internal.req.req_file import parse_requirements from pipenv.utils.constants import MYPY_RUNNING from pipenv.utils.dependencies import ( convert_deps_to_pip, @@ -30,7 +36,6 @@ from pipenv.utils.indexes import get_source_list, parse_indexes, prepare_pip_source_args from pipenv.utils.internet import download_file, get_host_and_port, is_valid_url from pipenv.utils.processes import run_command -from pipenv.utils.resolver import venv_resolve_deps from pipenv.utils.shell import ( cmd_list_to_shell, find_python, @@ -160,12 +165,6 @@ def cleanup_virtualenv(project, bare=True): def import_requirements(project, r=None, dev=False): - from pipenv.patched.pip._internal.req.constructors import ( - install_req_from_parsed_requirement, - ) - from pipenv.patched.pip._vendor import requests - from pipenv.vendor.pip_shims.shims import parse_requirements - # Parse requirements.txt file with Pip's parser. # Pip requires a `PipSession` which is a subclass of requests.Session. # Since we're not making any network calls, it's initialized to nothing. @@ -191,7 +190,7 @@ def import_requirements(project, r=None, dev=False): trusted_hosts = sorted(set(trusted_hosts)) reqs = [ install_req_from_parsed_requirement(f) - for f in parse_requirements(r, session=requests) + for f in parse_requirements(r, session=PipSession()) ] for package in reqs: if package.name not in BAD_PACKAGES: @@ -1124,6 +1123,8 @@ def do_lock( err=True, ) + from pipenv.utils.resolver import venv_resolve_deps + # Mutates the lockfile venv_resolve_deps( packages, @@ -1372,8 +1373,6 @@ def get_pip_args( selective_upgrade: bool = False, src_dir: Optional[str] = None, ) -> List[str]: - from pipenv.patched.pip._vendor.packaging.version import parse as parse_version - arg_map = { "pre": ["--pre"], "verbose": ["--verbose"], @@ -1388,10 +1387,8 @@ def get_pip_args( ], "src_dir": src_dir, } - if project.environment.pip_version >= parse_version("19.0"): - arg_map["no_use_pep517"].append("--no-use-pep517") - if project.environment.pip_version < parse_version("19.1"): - arg_map["no_use_pep517"].append("--no-build-isolation") + # TODO: Why do we use no pep517? + arg_map["no_use_pep517"].append("--no-use-pep517") arg_set = [] for key in arg_map.keys(): if key in locals() and locals().get(key): @@ -1407,11 +1404,9 @@ def get_requirement_line( include_hashes: bool = True, format_for_file: bool = False, ) -> Union[List[str], str]: - line = None if requirement.vcs or requirement.is_file_or_url: if src_dir and requirement.line_instance.wheel_kwargs: requirement.line_instance._wheel_kwargs.update({"src_dir": src_dir}) - requirement.line_instance.vcsrepo line = requirement.line_instance.line if requirement.line_instance.markers: line = f"{line}; {requirement.line_instance.markers}" @@ -1950,8 +1945,6 @@ def do_install( selective_upgrade=False, site_packages=None, ): - from .vendor.pip_shims.shims import PipError - requirements_directory = vistir.path.create_tracked_tempdir( suffix="-requirements", prefix="pipenv-" ) diff --git a/pipenv/environment.py b/pipenv/environment.py index 49dae5c23b..ed0d856ef9 100644 --- a/pipenv/environment.py +++ b/pipenv/environment.py @@ -14,6 +14,8 @@ import pkg_resources import pipenv +from pipenv.patched.pip._internal.commands.install import InstallCommand +from pipenv.patched.pip._internal.index.package_finder import PackageFinder from pipenv.patched.pip._vendor.packaging.utils import canonicalize_name from pipenv.utils.constants import is_type_checking from pipenv.utils.indexes import prepare_pip_source_args @@ -26,10 +28,8 @@ from types import ModuleType from typing import ContextManager, Dict, Generator, List, Optional, Set, Union - import pip_shims.shims import tomlkit - from pipenv.patched.pip._vendor.packaging.version import Version from pipenv.project import Project, TPipfile, TSource BASE_WORKING_SET = pkg_resources.WorkingSet(sys.path) @@ -539,21 +539,6 @@ def libdir(self) -> str: return "purelib", purelib return "platlib", self.paths["platlib"] - @property - def pip_version(self) -> Version: - """ - Get the pip version in the environment. Useful for knowing which args we can use - when installing. - """ - from pipenv.patched.pip._vendor.packaging.version import parse as parse_version - - pip = next( - iter(pkg for pkg in self.get_installed_packages() if pkg.key == "pip"), None - ) - if pip is not None: - return parse_version(pip.version) - return parse_version("20.2") - def expand_egg_links(self) -> None: """ Expand paths specified in egg-link files to prevent pip errors during @@ -641,41 +626,28 @@ def get_installed_packages(self) -> List[pkg_resources.Distribution]: return packages @contextlib.contextmanager - def get_finder( - self, pre: bool = False - ) -> ContextManager[pip_shims.shims.PackageFinder]: - from .vendor.pip_shims.shims import InstallCommand, get_package_finder + def get_finder(self, pre: bool = False) -> ContextManager[PackageFinder]: + from .utils.resolver import get_package_finder - pip_command = InstallCommand() + pip_command = InstallCommand( + name="InstallCommand", summary="pip Install command." + ) pip_args = prepare_pip_source_args(self.sources) pip_options, _ = pip_command.parser.parse_args(pip_args) pip_options.cache_dir = self.project.s.PIPENV_CACHE_DIR pip_options.pre = self.pipfile.get("pre", pre) - with pip_command._build_session(pip_options) as session: - finder = get_package_finder( - install_cmd=pip_command, options=pip_options, session=session - ) - yield finder + session = pip_command._build_session(pip_options) + finder = get_package_finder( + install_cmd=pip_command, options=pip_options, session=session + ) + yield finder def get_package_info( self, pre: bool = False ) -> Generator[pkg_resources.Distribution, None, None]: - from .vendor.pip_shims.shims import parse_version, pip_version - - dependency_links = [] packages = self.get_installed_packages() - # This code is borrowed from pip's current implementation - if parse_version(pip_version) < parse_version("19.0"): - for dist in packages: - if dist.has_metadata("dependency_links.txt"): - dependency_links.extend( - dist.get_metadata_lines("dependency_links.txt") - ) with self.get_finder() as finder: - if parse_version(pip_version) < parse_version("19.0"): - finder.add_dependency_links(dependency_links) - for dist in packages: typ = "unknown" all_candidates = finder.find_all_candidates(dist.key) diff --git a/pipenv/project.py b/pipenv/project.py index 951ca018e2..c7d716e8dd 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -23,15 +23,16 @@ from pipenv.core import system_which from pipenv.environment import Environment from pipenv.environments import Setting, is_in_virtualenv, normalize_pipfile_path +from pipenv.patched.pip._internal.commands.install import InstallCommand from pipenv.utils.constants import is_type_checking from pipenv.utils.dependencies import ( get_canonical_names, is_editable, is_star, + pep423_name, python_version, ) from pipenv.utils.internet import get_url_name, is_valid_url, proper_case -from pipenv.utils.resolver import pep423_name from pipenv.utils.shell import ( find_requirements, find_windows_executable, @@ -641,10 +642,8 @@ def pipfile_is_empty(self): def create_pipfile(self, python=None): """Creates the Pipfile, filled with juicy defaults.""" - from .vendor.pip_shims.shims import InstallCommand - # Inherit the pip's index configuration of install command. - command = InstallCommand() + command = InstallCommand(name="InstallCommand", summary="pip Install command.") indexes = command.cmd_opts.get_option("--extra-index-url").default sources = [self.default_source] for i, index in enumerate(indexes): diff --git a/pipenv/utils/resolver.py b/pipenv/utils/resolver.py index da4d455d81..3a07f1210b 100644 --- a/pipenv/utils/resolver.py +++ b/pipenv/utils/resolver.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import contextlib import hashlib import os @@ -7,12 +5,25 @@ import sys import warnings from functools import lru_cache +from typing import Dict, List, Optional, Set, Tuple, Union from pipenv import environments from pipenv.exceptions import RequirementError, ResolutionFailure -from pipenv.utils.constants import MYPY_RUNNING +from pipenv.patched.pip._internal.cache import WheelCache +from pipenv.patched.pip._internal.commands.install import InstallCommand +from pipenv.patched.pip._internal.exceptions import InstallationError +from pipenv.patched.pip._internal.models.target_python import TargetPython +from pipenv.patched.pip._internal.network.cache import SafeFileCache +from pipenv.patched.pip._internal.operations.build.build_tracker import ( + get_build_tracker, +) +from pipenv.patched.pip._internal.req.req_file import parse_requirements +from pipenv.patched.pip._internal.utils.hashes import FAVORITE_HASH +from pipenv.patched.pip._internal.utils.temp_dir import global_tempdir_manager +from pipenv.project import Project from pipenv.vendor import click from pipenv.vendor.requirementslib import Pipfile, Requirement +from pipenv.vendor.requirementslib.models.requirements import Line from pipenv.vendor.requirementslib.models.utils import DIRECT_URL_RE from pipenv.vendor.vistir import TemporaryDirectory, open_file from pipenv.vendor.vistir.path import create_tracked_tempdir @@ -32,11 +43,34 @@ from .shell import make_posix, subprocess_run, temp_environ from .spinner import create_spinner -if MYPY_RUNNING: - from typing import Any, Dict, List, Optional, Set, Tuple, Union # noqa - from pipenv.project import Project # noqa - from pipenv.vendor.requirementslib.models.requirements import Line # noqa +def get_package_finder( + install_cmd=None, + options=None, + session=None, + platform=None, + python_versions=None, + abi=None, + implementation=None, + ignore_requires_python=None, +): + """Reduced Shim for compatibility to generate package finders.""" + py_version_info = None + if python_versions: + py_version_info_python = max(python_versions) + py_version_info = tuple([int(part) for part in py_version_info_python]) + target_python = TargetPython( + platforms=[platform] if platform else None, + py_version_info=py_version_info, + abis=[abi] if abi else None, + implementation=implementation, + ) + return install_cmd._build_package_finder( + options=options, + session=session, + target_python=target_python, + ignore_requires_python=ignore_requires_python, + ) class HashCacheMixin: @@ -64,9 +98,7 @@ def get_hash(self, link): return hash_value.decode("utf8") def _get_file_hash(self, link): - from pipenv.vendor.pip_shims import shims - - h = hashlib.new(shims.FAVORITE_HASH) + h = hashlib.new(FAVORITE_HASH) with open_file(link.url, self.session) as fp: for chunk in iter(lambda: fp.read(8096), b""): h.update(chunk) @@ -122,18 +154,14 @@ def __repr__(self): @staticmethod @lru_cache() def _get_pip_command(): - from pipenv.vendor.pip_shims import shims - - return shims.InstallCommand() + return InstallCommand(name="InstallCommand", summary="pip Install command.") @property def hash_cache(self): - from pipenv.vendor.pip_shims import shims - if not self._hash_cache: - self._hash_cache = type( - "HashCache", (HashCacheMixin, shims.SafeFileCache), {} - )(os.path.join(self.project.s.PIPENV_CACHE_DIR, "hashes"), self.session) + self._hash_cache = type("HashCache", (HashCacheMixin, SafeFileCache), {})( + os.path.join(self.project.s.PIPENV_CACHE_DIR, "hashes"), self.session + ) return self._hash_cache @classmethod @@ -558,10 +586,8 @@ def prepare_index_lookup(self): @property def finder(self): - from pipenv.vendor.pip_shims import shims - if self._finder is None: - self._finder = shims.get_package_finder( + self._finder = get_package_finder( install_cmd=self.pip_command, options=self.pip_options, session=self.session, @@ -573,10 +599,8 @@ def finder(self): @property def ignore_compatibility_finder(self): - from pipenv.vendor.pip_shims import shims - if self._ignore_compatibility_finder is None: - ignore_compatibility_finder = shims.get_package_finder( + ignore_compatibility_finder = get_package_finder( install_cmd=self.pip_command, options=self.pip_options, session=self.session, @@ -595,12 +619,10 @@ def ignore_compatibility_finder(self): @property def parsed_constraints(self): - from pipenv.vendor.pip_shims import shims - pip_options = self.pip_options pip_options.extra_index_urls = [] if self._parsed_constraints is None: - self._parsed_constraints = shims.parse_requirements( + self._parsed_constraints = parse_requirements( self.constraint_file, finder=self.finder, session=self.session, @@ -628,12 +650,6 @@ def constraints(self): @contextlib.contextmanager def get_resolver(self, clear=False): - from pipenv.vendor.pip_shims.shims import ( - WheelCache, - get_build_tracker, - global_tempdir_manager, - ) - with global_tempdir_manager(), get_build_tracker() as build_tracker, TemporaryDirectory( suffix="-build", prefix="pipenv-" ) as directory: @@ -664,9 +680,6 @@ def get_resolver(self, clear=False): yield resolver def resolve(self): - from pipenv.exceptions import ResolutionFailure - from pipenv.vendor.pip_shims.shims import InstallationError - self.constraints # For some reason it is important to evaluate constraints before resolver context with temp_environ(), self.get_resolver() as resolver: try: @@ -712,8 +725,6 @@ def prepend_hash_types(cls, checksums, hash_type): return cleaned_checksums def _get_hashes_from_pypi(self, ireq): - from pipenv.vendor.pip_shims import shims - pkg_url = f"https://pypi.org/pypi/{ireq.name}/json" session = _get_requests_session(self.project.s.PIPENV_MAX_RETRIES) try: @@ -731,8 +742,8 @@ def _get_hashes_from_pypi(self, ireq): if spec: version = spec.version for release in cleaned_releases[version]: - collected_hashes.add(release["digests"][shims.FAVORITE_HASH]) - return self.prepend_hash_types(collected_hashes, shims.FAVORITE_HASH) + collected_hashes.add(release["digests"][FAVORITE_HASH]) + return self.prepend_hash_types(collected_hashes, FAVORITE_HASH) except (ValueError, KeyError, ConnectionError): if self.project.s.is_verbose(): click.echo( @@ -783,9 +794,7 @@ def resolve_hashes(self): return self.hashes def _get_hash_from_link(self, link): - from pipenv.vendor.pip_shims import shims - - if link.hash and link.hash_name == shims.FAVORITE_HASH: + if link.hash and link.hash_name == FAVORITE_HASH: return f"{link.hash_name}:{link.hash}" return self.hash_cache.get_hash(link)