From 4236a7130350906df6185190fea489fd736a6624 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Tue, 27 Oct 2020 16:09:56 +0800 Subject: [PATCH 01/12] Reduce the number of internet requests to get hash --- pipenv/patched/piptools/repositories/pypi.py | 2 +- .../vendoring/patches/patched/piptools.patch | 140 +++++++++--------- 2 files changed, 71 insertions(+), 71 deletions(-) diff --git a/pipenv/patched/piptools/repositories/pypi.py b/pipenv/patched/piptools/repositories/pypi.py index e81af1d501..c5a8cbf013 100644 --- a/pipenv/patched/piptools/repositories/pypi.py +++ b/pipenv/patched/piptools/repositories/pypi.py @@ -80,7 +80,7 @@ def get_hash(self, location): can_hash = new_location.hash if can_hash: # hash url WITH fragment - hash_value = self.get(new_location.url) + hash_value = self.get(new_location.url) or new_location.hash if not hash_value: hash_value = self._get_file_hash(new_location) if not new_location.url.startswith("ssh") else None hash_value = hash_value.encode('utf8') if hash_value else None diff --git a/tasks/vendoring/patches/patched/piptools.patch b/tasks/vendoring/patches/patched/piptools.patch index 1f0cca36d8..df93dea029 100644 --- a/tasks/vendoring/patches/patched/piptools.patch +++ b/tasks/vendoring/patches/patched/piptools.patch @@ -3,9 +3,9 @@ index fda80d5..4f7efbf 100644 --- a/pipenv/patched/piptools/_compat/__init__.py +++ b/pipenv/patched/piptools/_compat/__init__.py @@ -4,7 +4,37 @@ from __future__ import absolute_import, division, print_function, unicode_litera - + import six - + -from .pip_compat import PIP_VERSION, parse_requirements +from .pip_compat import ( + DEV_PKGS, @@ -38,7 +38,7 @@ index fda80d5..4f7efbf 100644 + user_cache_dir, + normalize_path, +) - + if six.PY2: from .tempfile import TemporaryDirectory diff --git a/pipenv/patched/piptools/_compat/pip_compat.py b/pipenv/patched/piptools/_compat/pip_compat.py @@ -54,19 +54,19 @@ index 9508b75..103b831 100644 +os.environ["PIP_SHIMS_BASE_MODULE"] = str("pipenv.patched.notpip") +import pip_shims.shims +from pip_shims.models import ShimmedPathCollection, ImportTypes - + -import pip -from pip._internal.req import parse_requirements as _parse_requirements -from pip._vendor.packaging.version import parse as parse_version +InstallationCandidate = ShimmedPathCollection("InstallationCandidate", ImportTypes.CLASS) +InstallationCandidate.create_path("models.candidate", "18.0", "9999") +InstallationCandidate.create_path("index", "7.0.3", "10.9.9") - + -PIP_VERSION = tuple(map(int, parse_version(pip.__version__).base_version.split("."))) +PIP_VERSION = tuple(map(int, pip_shims.shims.parsed_pip_version.parsed_version.base_version.split("."))) - + +RequirementTracker = pip_shims.shims.RequirementTracker - + -if PIP_VERSION[:2] <= (20, 0): +def do_import(module_path, subimport=None, old_path=None): + old_path = old_path or module_path @@ -86,15 +86,15 @@ index 9508b75..103b831 100644 + continue + else: + return getattr(imported, package) - + +if PIP_VERSION[:2] <= (20, 0): def install_req_from_parsed_requirement(req, **kwargs): return req - + - else: from pip._internal.req.constructors import install_req_from_parsed_requirement - + +InstallRequirement = pip_shims.shims.InstallRequirement +InstallationError = pip_shims.shims.InstallationError +parse_requirements = pip_shims.shims.parse_requirements @@ -121,7 +121,7 @@ index 9508b75..103b831 100644 +normalize_path = do_import("utils.misc", "normalize_path") +install_req_from_line = pip_shims.shims.install_req_from_line +install_req_from_editable = pip_shims.shims.install_req_from_editable - + def parse_requirements( filename, session, finder=None, options=None, constraint=False, isolated=False diff --git a/pipenv/patched/piptools/cache.py b/pipenv/patched/piptools/cache.py @@ -131,10 +131,10 @@ index 9b6bf55..983ddb6 100644 @@ -6,7 +6,7 @@ import os import platform import sys - + -from pip._vendor.packaging.requirements import Requirement +from pipenv.vendor.packaging.requirements import Requirement - + from .exceptions import PipToolsError from .utils import as_tuple, key_from_req, lookup_table diff --git a/pipenv/patched/piptools/locations.py b/pipenv/patched/piptools/locations.py @@ -144,19 +144,19 @@ index 9ca0ffe..37125c9 100644 @@ -1,12 +1,15 @@ import os from shutil import rmtree - + -from pip._internal.utils.appdirs import user_cache_dir +from ._compat import user_cache_dir - + from .click import secho - + # The user_cache_dir helper comes straight from pip itself -CACHE_DIR = user_cache_dir("pip-tools") +try: + from pipenv.environments import PIPENV_CACHE_DIR as CACHE_DIR +except ImportError: + CACHE_DIR = user_cache_dir("pipenv") - + # NOTE # We used to store the cache dir under ~/.pip-tools, which is not the diff --git a/pipenv/patched/piptools/repositories/local.py b/pipenv/patched/piptools/repositories/local.py @@ -164,15 +164,15 @@ index ec3a796..1aa29f0 100644 --- a/pipenv/patched/piptools/repositories/local.py +++ b/pipenv/patched/piptools/repositories/local.py @@ -3,9 +3,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera - + from contextlib import contextmanager - + -from pip._internal.utils.hashes import FAVORITE_HASH - -from .._compat import PIP_VERSION +from .._compat import PIP_VERSION, FAVORITE_HASH from .base import BaseRepository - + from piptools.utils import as_tuple, key_from_ireq, make_install_requirement @@ -65,7 +63,8 @@ class LocalRequirementsRepository(BaseRepository): if existing_pin and ireq_satisfied_by_existing_pin(ireq, existing_pin): @@ -190,14 +190,14 @@ index ef5ba4e..b96acf6 100644 +++ b/pipenv/patched/piptools/repositories/pypi.py @@ -2,28 +2,48 @@ from __future__ import absolute_import, division, print_function, unicode_literals - + import collections +import copy import hashlib import os from contextlib import contextmanager from shutil import rmtree - + -from pip._internal.cache import WheelCache -from pip._internal.commands import create_command -from pip._internal.models.index import PyPI @@ -253,12 +253,12 @@ index ef5ba4e..b96acf6 100644 @@ -32,10 +52,50 @@ from ..utils import ( ) from .base import BaseRepository - + +os.environ["PIP_SHIMS_BASE_MODULE"] = str("pipenv.patched.notpip") FILE_CHUNK_SIZE = 4096 FileStream = collections.namedtuple("FileStream", "stream size") - - + + +class HashCache(SafeFileCache): + """Caches hashes of PyPI artifacts so we do not need to re-download them + @@ -282,7 +282,7 @@ index ef5ba4e..b96acf6 100644 + can_hash = new_location.hash + if can_hash: + # hash url WITH fragment -+ hash_value = self.get(new_location.url) ++ hash_value = self.get(new_location.url) or new_location.hash + if not hash_value: + hash_value = self._get_file_hash(new_location) if not new_location.url.startswith("ssh") else None + hash_value = hash_value.encode('utf8') if hash_value else None @@ -300,11 +300,11 @@ index ef5ba4e..b96acf6 100644 + class PyPIRepository(BaseRepository): DEFAULT_INDEX_URL = PyPI.simple_url - + @@ -46,21 +106,29 @@ class PyPIRepository(BaseRepository): changed/configured on the Finder. """ - + - def __init__(self, pip_args, cache_dir): + def __init__(self, pip_args, cache_dir=CACHE_DIR, session=None, build_isolation=False, use_json=False): + self.build_isolation = build_isolation @@ -321,10 +321,10 @@ index ef5ba4e..b96acf6 100644 + self.options.build_isolation = build_isolation if self.options.cache_dir: self.options.cache_dir = normalize_path(self.options.cache_dir) - + self.options.require_hashes = False self.options.ignore_dependencies = False - + - self.session = self.command._build_session(self.options) + if session is None: + session = self.command._build_session(self.options) @@ -333,7 +333,7 @@ index ef5ba4e..b96acf6 100644 - options=self.options, session=self.session + options=self.options, session=self.session, ignore_requires_python=True ) - + # Caches @@ -73,6 +141,10 @@ class PyPIRepository(BaseRepository): # of all secondary dependencies for the given requirement, so we @@ -343,13 +343,13 @@ index ef5ba4e..b96acf6 100644 + + # stores *full* path + fragment => sha256 + self._hash_cache = HashCache(session=session) - + # Setup file paths self.freshen_build_caches() @@ -114,13 +186,15 @@ class PyPIRepository(BaseRepository): if ireq.editable or is_url_requirement(ireq): return ireq # return itself as the best match - + - all_candidates = self.find_all_candidates(ireq.name) + all_candidates = clean_requires_python(self.find_all_candidates(ireq.name)) candidates_by_version = lookup_table( @@ -363,7 +363,7 @@ index ef5ba4e..b96acf6 100644 + prereleases=prereleases) + except TypeError: + matching_versions = [candidate.version for candidate in all_candidates] - + # Reuses pip's internal candidate sort key to sort matching_candidates = [candidates_by_version[ver] for ver in matching_versions] @@ -136,9 +210,66 @@ class PyPIRepository(BaseRepository): @@ -373,7 +373,7 @@ index ef5ba4e..b96acf6 100644 + ireq.markers, constraint=ireq.constraint, ) - + + def get_dependencies(self, ireq): + json_results = set() + @@ -434,14 +434,14 @@ index ef5ba4e..b96acf6 100644 with get_requirement_tracker() as req_tracker, TempDirectory( kind="resolver" @@ -173,10 +304,11 @@ class PyPIRepository(BaseRepository): - + if PIP_VERSION[:2] <= (20, 0): reqset.cleanup_files() + results = set(results) if results else set() - + - return set(results) + return results, ireq - + - def get_dependencies(self, ireq): + def get_legacy_dependencies(self, ireq): """ @@ -465,7 +465,7 @@ index ef5ba4e..b96acf6 100644 cached_link = link - return {self._get_file_hash(cached_link)} + return {self._hash_cache._get_file_hash(cached_link)} - + if not is_pinned_requirement(ireq): raise TypeError("Expected pinned requirement, got {}".format(ireq)) @@ -260,38 +393,28 @@ class PyPIRepository(BaseRepository): @@ -480,7 +480,7 @@ index ef5ba4e..b96acf6 100644 - matching_candidates = candidates_by_version[matching_versions[0]] - - log.debug(" {}".format(ireq.name)) - + - return { - self._get_file_hash(candidate.link) for candidate in matching_candidates - } @@ -511,7 +511,7 @@ index ef5ba4e..b96acf6 100644 + if h is not None + } + return result - + - # Iterate over the chosen context manager - with context_manager as bar: - for chunk in bar: @@ -524,7 +524,7 @@ index ef5ba4e..b96acf6 100644 + yield + finally: + self.finder._ignore_compatibility = False - + @contextmanager def allow_all_wheels(self): diff --git a/pipenv/patched/piptools/resolver.py b/pipenv/patched/piptools/resolver.py @@ -534,12 +534,12 @@ index 0116992..550069d 100644 @@ -6,7 +6,9 @@ import os from functools import partial from itertools import chain, count - + -from pip._internal.req.constructors import install_req_from_line +from pip_shims.shims import install_req_from_line +from pipenv.vendor.requirementslib.models.markers import normalize_marker_str +from packaging.markers import Marker - + from . import click from .logging import log @@ -33,6 +35,7 @@ class RequirementSummary(object): @@ -548,7 +548,7 @@ index 0116992..550069d 100644 self.extras = str(sorted(ireq.extras)) + self.markers = ireq.markers self.specifier = str(ireq.specifier) - + def __eq__(self, other): @@ -63,6 +66,17 @@ def combine_install_requirements(ireqs): if combined_ireq.req is not None and ireq.req is not None: @@ -571,7 +571,7 @@ index 0116992..550069d 100644 @@ -337,10 +351,19 @@ class Resolver(object): if ireq.constraint: return - + - if ireq.editable or is_url_requirement(ireq): + if ireq.editable or (is_url_requirement(ireq) and not ireq.link.is_wheel): for dependency in self.repository.get_dependencies(ireq): @@ -595,12 +595,12 @@ index 0116992..550069d 100644 dependencies = self.repository.get_dependencies(ireq) - self.dependency_cache[ireq] = sorted(str(ireq.req) for ireq in dependencies) + self.dependency_cache[ireq] = sorted(set(format_requirement(ireq) for ireq in dependencies)) - + # Example: ['Werkzeug>=0.9', 'Jinja2>=2.4'] dependency_strings = self.dependency_cache[ireq] @@ -374,7 +397,8 @@ class Resolver(object): ) - + def reverse_dependencies(self, ireqs): + is_non_wheel_url = lambda r: is_url_requirement(r) and not r.link.is_wheel non_editable = [ @@ -614,24 +614,24 @@ index 03232a8..a7bfb4c 100755 +++ b/pipenv/patched/piptools/scripts/compile.py @@ -7,8 +7,8 @@ import sys import tempfile - + from click.utils import safecall -from pip._internal.commands import create_command -from pip._internal.req.constructors import install_req_from_line +from ._compat import InstallCommand +from ._compat import install_req_from_line - + from .. import click from .._compat import parse_requirements @@ -25,7 +25,7 @@ DEFAULT_REQUIREMENTS_FILE = "requirements.in" DEFAULT_REQUIREMENTS_OUTPUT_FILE = "requirements.txt" - + # Get default values of the pip's options (including options from pip.conf). -install_command = create_command("install") +install_command = InstallComand() pip_defaults = install_command.parser.get_default_values() - - + + diff --git a/pipenv/patched/piptools/scripts/sync.py b/pipenv/patched/piptools/scripts/sync.py index 137e813..4a7b3d5 100755 --- a/pipenv/patched/piptools/scripts/sync.py @@ -639,17 +639,17 @@ index 137e813..4a7b3d5 100755 @@ -6,8 +6,7 @@ import os import shlex import sys - + -from pip._internal.commands import create_command -from pip._internal.utils.misc import get_installed_distributions +from ._compat import get_installed_distributions, InstallCommand - + from .. import click, sync from .._compat import parse_requirements @@ -112,7 +111,7 @@ def cli( log.error("ERROR: " + msg) sys.exit(2) - + - install_command = create_command("install") + install_command = InstallCommand() options, _ = install_command.parse_args([]) @@ -662,12 +662,12 @@ index 430b4bb..015ff7a 100644 @@ -4,8 +4,8 @@ import sys import tempfile from subprocess import check_call # nosec - + -from pip._internal.commands.freeze import DEV_PKGS -from pip._internal.utils.compat import stdlib_pkgs +from ._compat import DEV_PKGS +from ._compat import stdlib_pkgs - + from . import click from .exceptions import IncompatibleRequirements diff --git a/pipenv/patched/piptools/utils.py b/pipenv/patched/piptools/utils.py @@ -677,12 +677,12 @@ index 7733447..e6f232f 100644 @@ -1,14 +1,19 @@ # coding: utf-8 from __future__ import absolute_import, division, print_function, unicode_literals - + +import os import sys from collections import OrderedDict from itertools import chain, groupby - + import six from click.utils import LazyFile -from pip._internal.req.constructors import install_req_from_line @@ -692,13 +692,13 @@ index 7733447..e6f232f 100644 +from pipenv.vendor.packaging.version import Version, InvalidVersion, parse as parse_version +from pipenv.vendor.packaging.markers import Marker, Op, Value, Variable + - + from ._compat import PIP_VERSION from .click import style @@ -25,6 +30,70 @@ COMPILE_EXCLUDE_OPTIONS = { } - - + + +def simplify_markers(ireq): + """simplify_markers "This code cleans up markers for a specific :class:`~InstallRequirement`" + @@ -768,8 +768,8 @@ index 7733447..e6f232f 100644 if ireq.req is None and ireq.link is not None: @@ -50,16 +119,51 @@ def comment(text): return style(text, fg="green") - - + + -def make_install_requirement(name, version, extras, constraint=False): +def make_install_requirement(name, version, extras, markers, constraint=False): # If no extras are specified, the extras string is blank @@ -777,7 +777,7 @@ index 7733447..e6f232f 100644 if extras: # Sort extras for stability extras_string = "[{}]".format(",".join(sorted(extras))) - + - return install_req_from_line( - str("{}{}=={}".format(name, extras_string, version)), constraint=constraint - ) @@ -819,8 +819,8 @@ index 7733447..e6f232f 100644 + parts.append("; {0}".format(requirement.marker)) + + return "".join(parts) - - + + def is_url_requirement(ireq): @@ -77,13 +181,15 @@ def format_requirement(ireq, marker=None, hashes=None): """ @@ -833,11 +833,11 @@ index 7733447..e6f232f 100644 else: - line = str(ireq.req).lower() + line = _requirement_to_str_lowercase_name(ireq.req) - + - if marker: - line = "{} ; {}".format(line, marker) + if marker and ';' not in line: + line = "{}; {}".format(line, marker) - + if hashes: for hash_ in sorted(hashes): From 52160d1a09a798a7c235a3cf96152f02d1f0397b Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Tue, 27 Oct 2020 16:18:53 +0800 Subject: [PATCH 02/12] Encode hash value --- pipenv/patched/piptools/repositories/pypi.py | 2 +- tasks/vendoring/patches/patched/piptools.patch | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pipenv/patched/piptools/repositories/pypi.py b/pipenv/patched/piptools/repositories/pypi.py index c5a8cbf013..8dbed6ead5 100644 --- a/pipenv/patched/piptools/repositories/pypi.py +++ b/pipenv/patched/piptools/repositories/pypi.py @@ -80,7 +80,7 @@ def get_hash(self, location): can_hash = new_location.hash if can_hash: # hash url WITH fragment - hash_value = self.get(new_location.url) or new_location.hash + hash_value = self.get(new_location.url) or new_location.hash.encode('utf8') if not hash_value: hash_value = self._get_file_hash(new_location) if not new_location.url.startswith("ssh") else None hash_value = hash_value.encode('utf8') if hash_value else None diff --git a/tasks/vendoring/patches/patched/piptools.patch b/tasks/vendoring/patches/patched/piptools.patch index df93dea029..9f507e2160 100644 --- a/tasks/vendoring/patches/patched/piptools.patch +++ b/tasks/vendoring/patches/patched/piptools.patch @@ -282,7 +282,7 @@ index ef5ba4e..b96acf6 100644 + can_hash = new_location.hash + if can_hash: + # hash url WITH fragment -+ hash_value = self.get(new_location.url) or new_location.hash ++ hash_value = self.get(new_location.url) or new_location.hash.encode('utf8') + if not hash_value: + hash_value = self._get_file_hash(new_location) if not new_location.url.startswith("ssh") else None + hash_value = hash_value.encode('utf8') if hash_value else None From 168237902eb56aea0c86c2c120bb0f69f6cdf600 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Tue, 27 Oct 2020 17:02:42 +0800 Subject: [PATCH 03/12] add news entry --- news/3827.feature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/3827.feature.rst diff --git a/news/3827.feature.rst b/news/3827.feature.rst new file mode 100644 index 0000000000..a3e3dced26 --- /dev/null +++ b/news/3827.feature.rst @@ -0,0 +1 @@ +Retrieve package file hash from URL to accelerate the locking process. From 41b31a439246bda73dc88ae2ea15c7cf14c71434 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Tue, 27 Oct 2020 17:06:20 +0800 Subject: [PATCH 04/12] revert format change --- .../vendoring/patches/patched/piptools.patch | 138 +++++++++--------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/tasks/vendoring/patches/patched/piptools.patch b/tasks/vendoring/patches/patched/piptools.patch index 9f507e2160..edf8132563 100644 --- a/tasks/vendoring/patches/patched/piptools.patch +++ b/tasks/vendoring/patches/patched/piptools.patch @@ -3,9 +3,9 @@ index fda80d5..4f7efbf 100644 --- a/pipenv/patched/piptools/_compat/__init__.py +++ b/pipenv/patched/piptools/_compat/__init__.py @@ -4,7 +4,37 @@ from __future__ import absolute_import, division, print_function, unicode_litera - + import six - + -from .pip_compat import PIP_VERSION, parse_requirements +from .pip_compat import ( + DEV_PKGS, @@ -38,7 +38,7 @@ index fda80d5..4f7efbf 100644 + user_cache_dir, + normalize_path, +) - + if six.PY2: from .tempfile import TemporaryDirectory diff --git a/pipenv/patched/piptools/_compat/pip_compat.py b/pipenv/patched/piptools/_compat/pip_compat.py @@ -54,19 +54,19 @@ index 9508b75..103b831 100644 +os.environ["PIP_SHIMS_BASE_MODULE"] = str("pipenv.patched.notpip") +import pip_shims.shims +from pip_shims.models import ShimmedPathCollection, ImportTypes - + -import pip -from pip._internal.req import parse_requirements as _parse_requirements -from pip._vendor.packaging.version import parse as parse_version +InstallationCandidate = ShimmedPathCollection("InstallationCandidate", ImportTypes.CLASS) +InstallationCandidate.create_path("models.candidate", "18.0", "9999") +InstallationCandidate.create_path("index", "7.0.3", "10.9.9") - + -PIP_VERSION = tuple(map(int, parse_version(pip.__version__).base_version.split("."))) +PIP_VERSION = tuple(map(int, pip_shims.shims.parsed_pip_version.parsed_version.base_version.split("."))) - + +RequirementTracker = pip_shims.shims.RequirementTracker - + -if PIP_VERSION[:2] <= (20, 0): +def do_import(module_path, subimport=None, old_path=None): + old_path = old_path or module_path @@ -86,15 +86,15 @@ index 9508b75..103b831 100644 + continue + else: + return getattr(imported, package) - + +if PIP_VERSION[:2] <= (20, 0): def install_req_from_parsed_requirement(req, **kwargs): return req - + - else: from pip._internal.req.constructors import install_req_from_parsed_requirement - + +InstallRequirement = pip_shims.shims.InstallRequirement +InstallationError = pip_shims.shims.InstallationError +parse_requirements = pip_shims.shims.parse_requirements @@ -121,7 +121,7 @@ index 9508b75..103b831 100644 +normalize_path = do_import("utils.misc", "normalize_path") +install_req_from_line = pip_shims.shims.install_req_from_line +install_req_from_editable = pip_shims.shims.install_req_from_editable - + def parse_requirements( filename, session, finder=None, options=None, constraint=False, isolated=False diff --git a/pipenv/patched/piptools/cache.py b/pipenv/patched/piptools/cache.py @@ -131,10 +131,10 @@ index 9b6bf55..983ddb6 100644 @@ -6,7 +6,7 @@ import os import platform import sys - + -from pip._vendor.packaging.requirements import Requirement +from pipenv.vendor.packaging.requirements import Requirement - + from .exceptions import PipToolsError from .utils import as_tuple, key_from_req, lookup_table diff --git a/pipenv/patched/piptools/locations.py b/pipenv/patched/piptools/locations.py @@ -144,19 +144,19 @@ index 9ca0ffe..37125c9 100644 @@ -1,12 +1,15 @@ import os from shutil import rmtree - + -from pip._internal.utils.appdirs import user_cache_dir +from ._compat import user_cache_dir - + from .click import secho - + # The user_cache_dir helper comes straight from pip itself -CACHE_DIR = user_cache_dir("pip-tools") +try: + from pipenv.environments import PIPENV_CACHE_DIR as CACHE_DIR +except ImportError: + CACHE_DIR = user_cache_dir("pipenv") - + # NOTE # We used to store the cache dir under ~/.pip-tools, which is not the diff --git a/pipenv/patched/piptools/repositories/local.py b/pipenv/patched/piptools/repositories/local.py @@ -164,15 +164,15 @@ index ec3a796..1aa29f0 100644 --- a/pipenv/patched/piptools/repositories/local.py +++ b/pipenv/patched/piptools/repositories/local.py @@ -3,9 +3,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera - + from contextlib import contextmanager - + -from pip._internal.utils.hashes import FAVORITE_HASH - -from .._compat import PIP_VERSION +from .._compat import PIP_VERSION, FAVORITE_HASH from .base import BaseRepository - + from piptools.utils import as_tuple, key_from_ireq, make_install_requirement @@ -65,7 +63,8 @@ class LocalRequirementsRepository(BaseRepository): if existing_pin and ireq_satisfied_by_existing_pin(ireq, existing_pin): @@ -190,14 +190,14 @@ index ef5ba4e..b96acf6 100644 +++ b/pipenv/patched/piptools/repositories/pypi.py @@ -2,28 +2,48 @@ from __future__ import absolute_import, division, print_function, unicode_literals - + import collections +import copy import hashlib import os from contextlib import contextmanager from shutil import rmtree - + -from pip._internal.cache import WheelCache -from pip._internal.commands import create_command -from pip._internal.models.index import PyPI @@ -253,12 +253,12 @@ index ef5ba4e..b96acf6 100644 @@ -32,10 +52,50 @@ from ..utils import ( ) from .base import BaseRepository - + +os.environ["PIP_SHIMS_BASE_MODULE"] = str("pipenv.patched.notpip") FILE_CHUNK_SIZE = 4096 FileStream = collections.namedtuple("FileStream", "stream size") - - + + +class HashCache(SafeFileCache): + """Caches hashes of PyPI artifacts so we do not need to re-download them + @@ -300,11 +300,11 @@ index ef5ba4e..b96acf6 100644 + class PyPIRepository(BaseRepository): DEFAULT_INDEX_URL = PyPI.simple_url - + @@ -46,21 +106,29 @@ class PyPIRepository(BaseRepository): changed/configured on the Finder. """ - + - def __init__(self, pip_args, cache_dir): + def __init__(self, pip_args, cache_dir=CACHE_DIR, session=None, build_isolation=False, use_json=False): + self.build_isolation = build_isolation @@ -321,10 +321,10 @@ index ef5ba4e..b96acf6 100644 + self.options.build_isolation = build_isolation if self.options.cache_dir: self.options.cache_dir = normalize_path(self.options.cache_dir) - + self.options.require_hashes = False self.options.ignore_dependencies = False - + - self.session = self.command._build_session(self.options) + if session is None: + session = self.command._build_session(self.options) @@ -333,7 +333,7 @@ index ef5ba4e..b96acf6 100644 - options=self.options, session=self.session + options=self.options, session=self.session, ignore_requires_python=True ) - + # Caches @@ -73,6 +141,10 @@ class PyPIRepository(BaseRepository): # of all secondary dependencies for the given requirement, so we @@ -343,13 +343,13 @@ index ef5ba4e..b96acf6 100644 + + # stores *full* path + fragment => sha256 + self._hash_cache = HashCache(session=session) - + # Setup file paths self.freshen_build_caches() @@ -114,13 +186,15 @@ class PyPIRepository(BaseRepository): if ireq.editable or is_url_requirement(ireq): return ireq # return itself as the best match - + - all_candidates = self.find_all_candidates(ireq.name) + all_candidates = clean_requires_python(self.find_all_candidates(ireq.name)) candidates_by_version = lookup_table( @@ -363,7 +363,7 @@ index ef5ba4e..b96acf6 100644 + prereleases=prereleases) + except TypeError: + matching_versions = [candidate.version for candidate in all_candidates] - + # Reuses pip's internal candidate sort key to sort matching_candidates = [candidates_by_version[ver] for ver in matching_versions] @@ -136,9 +210,66 @@ class PyPIRepository(BaseRepository): @@ -373,7 +373,7 @@ index ef5ba4e..b96acf6 100644 + ireq.markers, constraint=ireq.constraint, ) - + + def get_dependencies(self, ireq): + json_results = set() + @@ -434,14 +434,14 @@ index ef5ba4e..b96acf6 100644 with get_requirement_tracker() as req_tracker, TempDirectory( kind="resolver" @@ -173,10 +304,11 @@ class PyPIRepository(BaseRepository): - + if PIP_VERSION[:2] <= (20, 0): reqset.cleanup_files() + results = set(results) if results else set() - + - return set(results) + return results, ireq - + - def get_dependencies(self, ireq): + def get_legacy_dependencies(self, ireq): """ @@ -465,7 +465,7 @@ index ef5ba4e..b96acf6 100644 cached_link = link - return {self._get_file_hash(cached_link)} + return {self._hash_cache._get_file_hash(cached_link)} - + if not is_pinned_requirement(ireq): raise TypeError("Expected pinned requirement, got {}".format(ireq)) @@ -260,38 +393,28 @@ class PyPIRepository(BaseRepository): @@ -480,7 +480,7 @@ index ef5ba4e..b96acf6 100644 - matching_candidates = candidates_by_version[matching_versions[0]] - - log.debug(" {}".format(ireq.name)) - + - return { - self._get_file_hash(candidate.link) for candidate in matching_candidates - } @@ -511,7 +511,7 @@ index ef5ba4e..b96acf6 100644 + if h is not None + } + return result - + - # Iterate over the chosen context manager - with context_manager as bar: - for chunk in bar: @@ -524,7 +524,7 @@ index ef5ba4e..b96acf6 100644 + yield + finally: + self.finder._ignore_compatibility = False - + @contextmanager def allow_all_wheels(self): diff --git a/pipenv/patched/piptools/resolver.py b/pipenv/patched/piptools/resolver.py @@ -534,12 +534,12 @@ index 0116992..550069d 100644 @@ -6,7 +6,9 @@ import os from functools import partial from itertools import chain, count - + -from pip._internal.req.constructors import install_req_from_line +from pip_shims.shims import install_req_from_line +from pipenv.vendor.requirementslib.models.markers import normalize_marker_str +from packaging.markers import Marker - + from . import click from .logging import log @@ -33,6 +35,7 @@ class RequirementSummary(object): @@ -548,7 +548,7 @@ index 0116992..550069d 100644 self.extras = str(sorted(ireq.extras)) + self.markers = ireq.markers self.specifier = str(ireq.specifier) - + def __eq__(self, other): @@ -63,6 +66,17 @@ def combine_install_requirements(ireqs): if combined_ireq.req is not None and ireq.req is not None: @@ -571,7 +571,7 @@ index 0116992..550069d 100644 @@ -337,10 +351,19 @@ class Resolver(object): if ireq.constraint: return - + - if ireq.editable or is_url_requirement(ireq): + if ireq.editable or (is_url_requirement(ireq) and not ireq.link.is_wheel): for dependency in self.repository.get_dependencies(ireq): @@ -595,12 +595,12 @@ index 0116992..550069d 100644 dependencies = self.repository.get_dependencies(ireq) - self.dependency_cache[ireq] = sorted(str(ireq.req) for ireq in dependencies) + self.dependency_cache[ireq] = sorted(set(format_requirement(ireq) for ireq in dependencies)) - + # Example: ['Werkzeug>=0.9', 'Jinja2>=2.4'] dependency_strings = self.dependency_cache[ireq] @@ -374,7 +397,8 @@ class Resolver(object): ) - + def reverse_dependencies(self, ireqs): + is_non_wheel_url = lambda r: is_url_requirement(r) and not r.link.is_wheel non_editable = [ @@ -614,24 +614,24 @@ index 03232a8..a7bfb4c 100755 +++ b/pipenv/patched/piptools/scripts/compile.py @@ -7,8 +7,8 @@ import sys import tempfile - + from click.utils import safecall -from pip._internal.commands import create_command -from pip._internal.req.constructors import install_req_from_line +from ._compat import InstallCommand +from ._compat import install_req_from_line - + from .. import click from .._compat import parse_requirements @@ -25,7 +25,7 @@ DEFAULT_REQUIREMENTS_FILE = "requirements.in" DEFAULT_REQUIREMENTS_OUTPUT_FILE = "requirements.txt" - + # Get default values of the pip's options (including options from pip.conf). -install_command = create_command("install") +install_command = InstallComand() pip_defaults = install_command.parser.get_default_values() - - + + diff --git a/pipenv/patched/piptools/scripts/sync.py b/pipenv/patched/piptools/scripts/sync.py index 137e813..4a7b3d5 100755 --- a/pipenv/patched/piptools/scripts/sync.py @@ -639,17 +639,17 @@ index 137e813..4a7b3d5 100755 @@ -6,8 +6,7 @@ import os import shlex import sys - + -from pip._internal.commands import create_command -from pip._internal.utils.misc import get_installed_distributions +from ._compat import get_installed_distributions, InstallCommand - + from .. import click, sync from .._compat import parse_requirements @@ -112,7 +111,7 @@ def cli( log.error("ERROR: " + msg) sys.exit(2) - + - install_command = create_command("install") + install_command = InstallCommand() options, _ = install_command.parse_args([]) @@ -662,12 +662,12 @@ index 430b4bb..015ff7a 100644 @@ -4,8 +4,8 @@ import sys import tempfile from subprocess import check_call # nosec - + -from pip._internal.commands.freeze import DEV_PKGS -from pip._internal.utils.compat import stdlib_pkgs +from ._compat import DEV_PKGS +from ._compat import stdlib_pkgs - + from . import click from .exceptions import IncompatibleRequirements diff --git a/pipenv/patched/piptools/utils.py b/pipenv/patched/piptools/utils.py @@ -677,12 +677,12 @@ index 7733447..e6f232f 100644 @@ -1,14 +1,19 @@ # coding: utf-8 from __future__ import absolute_import, division, print_function, unicode_literals - + +import os import sys from collections import OrderedDict from itertools import chain, groupby - + import six from click.utils import LazyFile -from pip._internal.req.constructors import install_req_from_line @@ -692,13 +692,13 @@ index 7733447..e6f232f 100644 +from pipenv.vendor.packaging.version import Version, InvalidVersion, parse as parse_version +from pipenv.vendor.packaging.markers import Marker, Op, Value, Variable + - + from ._compat import PIP_VERSION from .click import style @@ -25,6 +30,70 @@ COMPILE_EXCLUDE_OPTIONS = { } - - + + +def simplify_markers(ireq): + """simplify_markers "This code cleans up markers for a specific :class:`~InstallRequirement`" + @@ -768,8 +768,8 @@ index 7733447..e6f232f 100644 if ireq.req is None and ireq.link is not None: @@ -50,16 +119,51 @@ def comment(text): return style(text, fg="green") - - + + -def make_install_requirement(name, version, extras, constraint=False): +def make_install_requirement(name, version, extras, markers, constraint=False): # If no extras are specified, the extras string is blank @@ -777,7 +777,7 @@ index 7733447..e6f232f 100644 if extras: # Sort extras for stability extras_string = "[{}]".format(",".join(sorted(extras))) - + - return install_req_from_line( - str("{}{}=={}".format(name, extras_string, version)), constraint=constraint - ) @@ -819,8 +819,8 @@ index 7733447..e6f232f 100644 + parts.append("; {0}".format(requirement.marker)) + + return "".join(parts) - - + + def is_url_requirement(ireq): @@ -77,13 +181,15 @@ def format_requirement(ireq, marker=None, hashes=None): """ @@ -833,11 +833,11 @@ index 7733447..e6f232f 100644 else: - line = str(ireq.req).lower() + line = _requirement_to_str_lowercase_name(ireq.req) - + - if marker: - line = "{} ; {}".format(line, marker) + if marker and ';' not in line: + line = "{}; {}".format(line, marker) - + if hashes: for hash_ in sorted(hashes): From f0ebbfc443f089edb2106259c3c6f6c84f0edacc Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Tue, 27 Oct 2020 17:42:26 +0800 Subject: [PATCH 05/12] Don't load packages from pypi --- tests/pytest-pypi/pytest_pypi/app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/pytest-pypi/pytest_pypi/app.py b/tests/pytest-pypi/pytest_pypi/app.py index 6f829819ba..f1812af3bd 100644 --- a/tests/pytest-pypi/pytest_pypi/app.py +++ b/tests/pytest-pypi/pytest_pypi/app.py @@ -155,9 +155,9 @@ def prepare_packages(path): packages[package_name] = Package(package_name) packages[package_name].add_release(os.path.join(root, file)) - remaining = get_pypi_package_names() - set(list(packages.keys())) - for pypi_pkg in remaining: - packages[pypi_pkg] = Package(pypi_pkg) + # remaining = get_pypi_package_names() - set(list(packages.keys())) + # for pypi_pkg in remaining: + # packages[pypi_pkg] = Package(pypi_pkg) @app.route('/') From 1249e20a95ddfb0ff1aab6410e286a3bf8d2e443 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Tue, 27 Oct 2020 17:53:57 +0800 Subject: [PATCH 06/12] Change the KR url --- tasks/vendoring/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/vendoring/__init__.py b/tasks/vendoring/__init__.py index 87d93586f1..d3cbbd6b04 100644 --- a/tasks/vendoring/__init__.py +++ b/tasks/vendoring/__init__.py @@ -50,12 +50,12 @@ HARDCODED_LICENSE_URLS = { "pytoml": "https://github.com/avakar/pytoml/raw/master/LICENSE", "cursor": "https://raw.githubusercontent.com/GijsTimmers/cursor/master/LICENSE", - "delegator.py": "https://raw.githubusercontent.com/kennethreitz/delegator.py/master/LICENSE", + "delegator.py": "https://raw.githubusercontent.com/amitt001/delegator.py/master/LICENSE", "click-didyoumean": "https://raw.githubusercontent.com/click-contrib/click-didyoumean/master/LICENSE", "click-completion": "https://raw.githubusercontent.com/click-contrib/click-completion/master/LICENSE", "parse": "https://raw.githubusercontent.com/techalchemy/parse/master/LICENSE", "semver": "https://raw.githubusercontent.com/k-bx/python-semver/master/LICENSE.txt", - "crayons": "https://raw.githubusercontent.com/kennethreitz/crayons/master/LICENSE", + "crayons": "https://raw.githubusercontent.com/MasterOdin/crayons/master/LICENSE", "pip-tools": "https://raw.githubusercontent.com/jazzband/pip-tools/master/LICENSE", "pytoml": "https://github.com/avakar/pytoml/raw/master/LICENSE", "webencodings": "https://github.com/SimonSapin/python-webencodings/raw/" From 491fa333b1cf13b352407b499d39b3a8295bbfe7 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Tue, 27 Oct 2020 18:29:51 +0800 Subject: [PATCH 07/12] fix the hash value --- pipenv/patched/piptools/repositories/pypi.py | 5 ++++- tasks/vendoring/patches/patched/piptools.patch | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pipenv/patched/piptools/repositories/pypi.py b/pipenv/patched/piptools/repositories/pypi.py index 8dbed6ead5..8f742713b4 100644 --- a/pipenv/patched/piptools/repositories/pypi.py +++ b/pipenv/patched/piptools/repositories/pypi.py @@ -80,7 +80,10 @@ def get_hash(self, location): can_hash = new_location.hash if can_hash: # hash url WITH fragment - hash_value = self.get(new_location.url) or new_location.hash.encode('utf8') + hash_value = self.get(new_location.url) + if not hash_value: + hash_value = "{}:{}".format(new_location.hash_name, new_location.hash) + hash_value = hash_value.encode('utf8') if not hash_value: hash_value = self._get_file_hash(new_location) if not new_location.url.startswith("ssh") else None hash_value = hash_value.encode('utf8') if hash_value else None diff --git a/tasks/vendoring/patches/patched/piptools.patch b/tasks/vendoring/patches/patched/piptools.patch index edf8132563..7470e13301 100644 --- a/tasks/vendoring/patches/patched/piptools.patch +++ b/tasks/vendoring/patches/patched/piptools.patch @@ -282,7 +282,10 @@ index ef5ba4e..b96acf6 100644 + can_hash = new_location.hash + if can_hash: + # hash url WITH fragment -+ hash_value = self.get(new_location.url) or new_location.hash.encode('utf8') ++ hash_value = self.get(new_location.url) ++ if not hash_value: ++ hash_value = "{}:{}".format(new_location.hash_name, new_location.hash) ++ hash_value = hash_value.encode('utf8') + if not hash_value: + hash_value = self._get_file_hash(new_location) if not new_location.url.startswith("ssh") else None + hash_value = hash_value.encode('utf8') if hash_value else None From 7bb6668cdfb55f7f639abf1e7334a582a09d1644 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Wed, 28 Oct 2020 09:57:49 +0800 Subject: [PATCH 08/12] Update piptools patch --- .../vendoring/patches/patched/piptools.patch | Bin 32947 -> 68994 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tasks/vendoring/patches/patched/piptools.patch b/tasks/vendoring/patches/patched/piptools.patch index 7470e133011dfdc5b35a8ee2a8124ea7ff4873ef..365010055313b663214432f840e60c7bd5db698f 100644 GIT binary patch literal 68994 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 literal 32947 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 NL)dE2m_`tY{|Cb>QDguB From d34feeece48caae803ab6899a848071e20643db1 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Wed, 28 Oct 2020 10:05:12 +0800 Subject: [PATCH 09/12] Fix the file encoding --- .../vendoring/patches/patched/piptools.patch | Bin 68994 -> 33568 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tasks/vendoring/patches/patched/piptools.patch b/tasks/vendoring/patches/patched/piptools.patch index 365010055313b663214432f840e60c7bd5db698f..b1694a6c468ae7ad30b0540efe0efddf070c217d 100644 GIT binary patch literal 33568 zcmb_lZF3vBk^atKF-KLMs3kQPMZG85%AT!_eev2p$=*#)4-lUc|Iwg z^oRK!9zN;!vuU>I_v6`pH29X%4)G@EDB1sY6;3;DgAX2X1x_9xjQohOZkf+(`jjmECPk3mCAJuQ9n;!Xe8 zi_5DQuU`RVGB48pJpI3AHc#);>7r<~*EMW4+7Uj_E`II5`OBZLI_BrkKmGdp^2OVW z{_~%%p4(?(7q(VDze^TR^XX!qPi(Ch(_)cKCJ6`gG?|XFQL;$OWl!_FJCu|kN`s5} zJfD}JU;0tlf|uF!gZ=s@8GcA^(x1r)+V0K$n-{h?XnU0|Rv*5dqrGc*nPQGUx}R6c z7=S0~(*$g5+x|K%u9h>-sD1F;ZJO%H<$G9rcsI&tq(fnA-=quhy3i*b^+#E;m}i4! zSi_cb?)sduemcq)$zY-gtdvi(sg|wIy6qN=?6$wi`^&kaO8&t}g#pb}7K_m&8}w%% zZWLM6W+OJXaF#_n?+-~1;I8DJk_RT)KU1x_LzW#!VAuG~-}cWM2Z3vcQQDJ^hlG#?!kWd+oVSs;%4%r z+=ddQ+u7fb+TG6aK14_(+KP66*o`0rvgytFaxvaL;e*D8vZc|sGROSEthy(;%7J7y z!weQW59Nt%wDTf{#LnjV^t#DafA##uFIWBl`swPT|I6!V?_ORs-$&;WxVP2BU{E!- z!abhm3p{D=xGp)^;x@Z0V)@s~A^-FaqHf|4xkAmmbo2&1_cWhOz!M@8Ux*NWdp}DH zh(J3$z_8*F-(svQh|6g*iLrEaT&pW0N{R@Kx*{HpTophc)9EOmV?-1!n1R?XloC+F zF<@+Iqg^ZbK&IL;wVJino1G{aYy9-(Pghrf^)T2n|6p1$ccVVwJ81Ti0$<-#qaADrR`z2cgcY!8yjqD& zH;w{4ug~`1M}ItzTKjvQXm97N(H^DaXq4OSy@lCWPOL#o;+`~z5P&A#}_u*57-qoEgz^2 z3c-4+X~V~jc3>pb)Co!(ZM7$B>SfhZ7+kncth)g<_jNYzk@>wf+W2gYhA(ztfZ6Xf!z5 z%=&Xc9ya(~HQIPb^b+wO{4o0~6gcPHnPIHZO77f);-03CZcwrEy|+rZ0@S;s00R;2 z4J~eND>ltzt*x;c8}))N4DI7hHhW9=o#+}`O&C^dBt>6rgHH4zy*FMAbk~U{dH!KJ z>x(;Mv-zClL)qJkhvzh`rt>;X_QvBhj(bPL;bHG&vw1B6dU$S2RgDeb6&t>LC^mdB z#WIP-Eor1An%^zvXVyFyo33uwusRET^s4JmmHrS#c?-D_D{!qo2`v{hz~E4`ZioGz0PUW?ipz)v-$hF%mpMZ zpY-zrc9imkeFEn*oW}Eqmfvks++q#bRb19hchw7DU4MatK>h6xROv)zu+~RP_l5TR zU-*8hGk*`xQ^uDS7C$C+u{}WS-X$LpcJM}-?ivl*l@4WF>UB=IFAXP2QA97vUcpvU zcobS?j6%x|=3)979&LEc;GLO9a70B!pkF|Q7vl_0o56iQtkN?0g^xmjH2P-q{2y=v zb|R}(!O+Ii)D5OpYU+(hwzP)iy1}=MQOTP6S?!DXVF!d9EIJU1G&Ds+k>~!mx9T?p zCGGE$IYJNWY}c0j>mi?4dkU*XJATCUZv<8}t62h2V#83j(&Ij+4%Qz)b?zf_GrYBP z0JqRQoupv5V&^*q((HbgeS_n^a zJ&~h!SN0}K=nT_g*tm3gG)C0c4=IST*36YHt$JXfq?*}q(Vn2KBg3Vi`<42|EFEUp z-n{$qN}qHh_fy!dJ)RH=2@eNdp@(8@oulzhf)fR9e*{@}uvJbyIUS4wWSzt9Y zZsyFpQ*2FHvf=lqL7d$xY7m6BM#zW;VBk!#bprmx6P?*OcBY!LmQ(Z9LW)Z!5)^@( zIowKvWbP`g+QF}Km)-Dl4n^Npo#ZzR8)|3x>557Uo1OJhI$F-`2!_~#rz&ZR{_K8n ziwqI#PbvD)KkSbTh1qlpoho2gUq(ul3Pk1J?{xQ~_Thf#ut#N2_&zQxk?`F>t1u-G zDWbneggE;7#mkHS)93GA{iT2P;(sro*d6Sh9yKB+U0lIFPVVq1*f3+dlSU@&*z}d? zA_HYuy+9fQEi|}JMU#Dw%~aT`RpM2Rxiy!^u5$$W=>KHUW}FNcFoN^wQwrCd z#DS(M!ZR@S=IQPz|1_QC34Y$Dca64TNp=ULA%Pv3Pbc@r5FEkcOQ&KOzPo%G!LCPy ztRNUNIrPaWk?tAnV!9O6W^Y8ofr975$#R6qw*>H5Mr@;x$z+*YEe~kPoUsr&8c>#Z zV%YZxm*q=Z@aQc0m}jG?DBv|ELe^B(r2%}|tLef(h=#Yx^d>E0P{}-}yH9hqTGT5M z!eRWT6v+`Jsef9pAEwYmdE!|yb(Ia#!}v5}c{ zk!{2u;T%G&ApS?5Ou*gWyjr|GA@ z7Rth#-N&Oeol!k)X+Z!M;|w8#eSt)b)GymtQHe2z@MB0Q^ih4Ji2-e_>F&<;Zr8?l zIYMMM2<@xQNEAN^Af**6-EWx*V8_3`c>5d+Cb^;0$hT!LB@zo~V$NGtJOe7Q5jtg< z*te~8y#g4K_p>iP5MBZ=P>kbz<$0yw6}TP)xW$-6*xE+=&`G%39>djx0HK8deR4gs zwhZfsNbTs*!-t&P7&&h9Me!+H+_suUaogOfhTw@iKc)Wsj|%?m#~0SYT0iktzO2q- zCjc2x)*8;fJFJ3`mg{jbtR!NxOf#ZD|F@9@p}1fk2YX_$A4F`Gbie>9QZmYsY9&i_ zg8T|>@OL@Ve>gL4c{11nd*8~?9cC%D#xp#{#u#=>$}weubg z@t<`)arb@H+w|<>=bzraeA|EV>eO@kwd4R-_0wKiyZC)T@S5X2)YFY4kVS!+uMxW`N6AU#8 zh{oPEC`7PM4i3;H+}Q`Mrgf?^(xI>`Ez0I5Hf616=0mfC#j6=MHEC|bJd%(lX)?YS z2s^1=nY}PSs9O{STf&*P=3~p}GlqX=gO<8_L+y60*S@3r$RJ0#rOE zqpc8IGZ$nq-v(Wpg!l=p%8*SY9iyx3z$T($XP}^}c)Bg^&qFf15WRV7JNN9ysoA_8 zmZX^oJ9%oyp1++V3r(jPcBcQn6+NZ#Ux?H`?sbkJv=0twuDYPaMnapww<1_^&_Y2P z2$&I)%dx-%j^MUOJLx7vP#pG?^Xh>LH1jeDMbiEU>;%J9#8?Wn zLm0lX(6_{kCUYKh_T{T5t*J(l&AO#3zS5QnE*8-bD+w!+O6Yx|mXGqmRmOkw&xS#bSFvswWz@D`$MPL_@YjiMl zyO^>brtajlBT?{g+0@wy7M27&G{pq=1wzd_W)ZrN-}d&MP9Q(l-+a)T)sNw?yH2t+}-3R>5CaEu_4O{?4>fEjEJ9vw!^ zd^%B>KVg$>lm=z6l6Y}$w7F<_77aCp9X{M?hQz6rr6?Je1SaHR2+wv)ipZQeHzy)U z6Do^dqOx7fA+3EG{Sq6k8~~z@Sw@oi#XjnE9(?zZ14{egKfB{+b5y?vIlq)lZ=3!b zj3XRVFcO=6z|#zM`MPz(y8dSBzAs7F%M|Jx^e@rEG(tl|*-lQuBWmz+S2S}TUiqc4xmG*ic&~LpT=Og2Z=nuHVI)Bs_3!{K)h1_6+r~^ zNWD))Ky@)y#VcCJ>Iz|_6`)Xeh>vbLPE|5uwM>LZShW^@1?fq8lML@6zx}W*2DYR` z7$J$rAw*uS%`(6efvnUITPgi?8dir*dGF`K_YG>(sne=;wHsoElSf)0I{>zhTiC7b zi_ZuJ!2rR^qq9D;=D=mBbljkmFJHIy_=QlQG%ADo)O1;yb7hHDlbE<;3A)`37OOLi zs-8K)T}}Wfx%h-1ddtq?N?j~w#n}@RXV2$1^5qH6b37qEOSnr?KHXP=#3Lk4FA&D| zE($rhf?jrFLTc??Z)?SE`2PHC5QK;m(dmOzB`n~_n3W=S6hgT*D}h|w=jXuNp*-J- z&avn7%mY@VAn$g%$_N5qq=iKqTcj%U!U~2PtAnt}sY2s44y%g^JysEaU~OG8Bc{7hyl4tTn^hJ zDWta<7%8^fMpZnp4L|o;Izet!$Qi4&1aNYsLY8&xgtoC0YKBu+?V50SUi11!HfKa8 zdD6!Ul$BLxg($);Ms2NhYM=$wurqZb=aArvs$dm>LnEVDP@;iFc-Wfjps@R)#?8Z(3G||M3O;9tT;!?rH!+{O=5j{ow3OS=6VgPSYbm>o5f-+vdh`cJi#>vRFxL#&HZ_Ek?)#Z!24Yq z*3AlVYed5QXPCOTciicAH`fl!)WYfsGAC9}Ybv4KUvgo(KpFtz??_Rn^-HVPiKI|{ zSwJR;G9%FsEs+z3Tr^PpoHZ3NzqScs%9%S`U>H8|C2ycKs!~&@NtM(ZH%_{%-H$(u z2PCn|Nhg9Q9pTh@9g$iu zw^{^txx$5s_fcgF0M{MEy}bCJ{@cr+p8nCehcUPKZkd z@QfFpo})rQCAI>VHSNOcrSFdj;??o6b+B4OV2=*SBDx9`{t|c$a7;D?=e}cw%Yo;R z(%jhmY^-TW2xuHm;+bJPWX9l#PVeO8-xB(SFK#gtp#bTbfYk~TnBb0pv(uabM`i*W zls+}dyjorc%m|1VHd@CDoiB#+6eAR8CU_gP-}$=AIOV3^dl*+@z-2Ig1fZ+f9d+4B zYQHF&^0#U+HLDg=u)iafQA{Owew=y@=ttM%{?T3shkV+-Q-u72RQ8RxY&puI`8}YS zc#z>prr*%H8R$k#tjdqf8iY@mh#MhtWt|J25L=XJ9kzx+_$h-o8u`lLbP03emxV0* zi~%~#mrx3krd?!%3DSY}1K+RY&%-yvd>-$62*Oll$C@tohy9J7Z~X8AUk-qj+>PZf z);bt>JXo&{3 zDRd{Ok;)g3(!uhk)r?@Xml)A_hHEzeFTOqJw0>1uM_kMmZ7xJ`C{n07%-qw$L;b2K zQXQx)RQ86%146aO=!rvV9FYVDn6Ma(h%K1M(Wj!FFCju~whY#rBgZ4T3$ET{xeMrv z5h(8z5Cwi5AWWnYBVw|GWX{RWSc}x7!U~dxApYu7E(20gm>Xy^8Y(G=Hn~9wNGSZU zt31cbO(aJTHXdd`(xz=FiPS>I9Qb6wQ(5!r_P73h>nQZt!@J#cDD%pn93k&V!EeEE%Meb1*a% zl@eI94V50%_hkE)6c)s%>`&Wipca^&9;k%@Fkudve2AF*lLTGJeNewEmSoK$04$qD zD@3sW3{B{P3+LYm$2i?zL1P|VAce5YlB>L#2BT*4(t-(E@S(+0Pvh}LlN2bgK?r}QA#L?Ff$HiPNdrEAt z+dVox-H+qLL)`9oI@PEJ#J+e28^d#Uhz0*@)6Lq{=ihh{2mevz|{2tk^Y1PV${S?EK^#Og5# z(!D3yA3gft@Y6WnE`7Y@YKenr^ZQml;JHvRop2@p5%$KQu|Pzu;%lXl0ZwG2>zxFw zWsH(aJ@M&^V;*4vhC|bli4K^8QZhNU79G6p*#4{lbt*1U00j*|_fS(XM1|{P=K9tr znnO%7X;4^FNON@5=?ZOF7ouSRj;K4{TSynB%ayo;nFdH5#ON9B2y@0HEv~L0ATvq5 z0>mru(SLgK`=_23<)((^u{aj4oqy!z8SoaYl+euHqn%j>W=kttLv0<*0khhn%-i(# z5PY!zrIWg@Ye81Jg(4=$d(qSNd+vP;g!|9n3MdxWzerIf;K7Z{w4rVx?jB_m6 zGTjH{WJU9kLIk8~3dd=haGj)S-5GP}j#SYOILtdH4r|Z+TT3JKQ5q5Z4LmMe(WSZg z4Bkvx=eU;_XM1o$1j(Sh-jIg6S@r8!*a-`BF3DFTPF8r(Eo_R*NLqPA(JO8W0mb8(|A_PrdipWfgyVQI4ahGGgVNDL^Fu+ z4_B&xR9OgY^S~quCDj<$qv{%q;D23 zT3nSIss~1Ot62-|m!I*?dGkJ{fw>xWGBxbKj#W(ZuvID^VZ5@zB5fR;`>Oxm=o3x_ zz`%`!-_rSqf2PZu zAJ1_QERNf4|93Y1N3#Fp`F?zWpuHoWL;IdG78a$;YzRukZ)`#tWvGO_kX=RX$1OOBn<>(4O;pGvwpdenepV;$kQ$Yb%rD{l_{k()4Ane7!8_PgkQ01{eaA^u)%kLEe+i8La+@6(`fDAT26zySVQfQ z1RLHRQVi#K2S9zCns?{KIsj*y_6Q=@!{bk*Z$Xld)P3n zX+UM19NV^RYwbf`*`}rruUnvZUu_^UeadI83*0Y{i*_cp1|diTZ^AcSxIu3ERvWT< zJ&}$0J~h4TeFc?k#_BChwd-OfLCM;ycC>nJpo!xgkgWUs7eqGJWBh4Oi(0|HUM^<4S6+%Yi4vxObI+c9MbW11F1qm#%5` z!-vGMqrIYCk#ToPdSpBkfCG`wfLInT7_kSlfNTYQb5?0CS*5m?Ii$=CipFMMAzfs} z{dBmQIIuOXEa>)*)01uwuK-Do2R$6F{w7(Vur?C}T3bwVvg_JQ9dWEP{p>_S^+rd{ z!^TW}^IBiME`}vpXU$88*c+Z!AV264RCJsgjZlg<3G3geNLW~6}Z2(lsA z{otW})@&BIsU2}I<`wC70g*TS0394bcPO?~#2be7E?8ZK$o))H1mbLG?y}n~2_tNO(yyiO(($TYQhyyfIX+Zp z_dipYtYg97L+evv23SGJz^*8~#^(+JJGY=@Q|tfV5_b=17{I&&X624y=iPQ~uQbo& z3U6bAn8jTt*~mS71RQM-FuRiMSM$?>ald8Xsd+z(s$oF{#NVcD+>#Da<239bn(}x zFW)`8=)ZpR7O(NT!WQtQf#@nu5^63fB%==`vU3lcU=Ettmvmqj}@a>F0I0~!i?rxh_Ti>!vx?i zCR(Xy(N9y|O#_HLW+st&1NMx*aa-Tv0i83r9uKe>tWXUnuEEm6G6w9rj!iI`!+>MY zOtQUK>1=L1^#3jb%&@iOvUYyiAGmGF1y{R8C*rw;nb{DuzsK$6CY~Z4GM-Pv{o3(bVdtMy+ z9tJ``e8Ud_?*ws<8n2{>{)+@FY~(>bU*uA8{o8u6GJk+1a^|NuNN_Y&0%8#R3PE_@ z?_K+un$R+uM-+jC)!jQ~2&Q zoLS&d&10F1_toIbDpu0M_JTDh=e_O%!Vrn__$`g_ejmKR?om3heVMpAju1}8ImJe+ zsICXKIZ47!OVlajNCxQNDSH~Sn&?r9PXe~AnJ5#FDFg24jM(~E1n6s9r(-stpW*@81$qMq1Y=4lXbh;o1 z&&q3&b#S^+m#^gIid+|MLbo)zkJDVc_je4(oJlhmD*ioUe1$bVglVy}fwWfAhDu&tJdN7jx2m9$QvnrWQDeCUl`k z=CQqv^Xs!7xLbLRYaCa?B1a0Qshp$_4{u_wbaxpd)KnSgnxNBG^ctDQy7S9DqPzeR z$;WBVkSC&*yu{n&8}mT8h*w;Efw#HZ&63NRpDi45vbWi%c-sQr*7q^RX%0?Kfg_+) z!nS`rmzJGB%Aa=EGP?mF0t#7)NJy(7FXuA5vba{tNRL)?pBF#&YOkhRZGnaQBM*Ld z{`jT)AB8{vxl$A7aHfkoblSL)&TzJ(zM8$o5=&TyCAc#??G`F;4cIb7dzP-M7zYbb z`>q$UH5)7&fK3?83o+yJ=H-(`%d>ZN16$u$d9CQKFtqfsE5BSx);~R{bqP2&K|v4;-yW z$UtyY6r>kyMOTP#Dmcy>rqU!X8eoLD0`U6F*Y_@&zS*()AU1iz)TTf*{yo)_r{bV2 zr?~}Z3ow)xu37x@73aNuroNep5(4hs0z1eC_nY3i|7!a6+asCC(mYt1598&ckR$+c z+g9A%QS?V}D@zKGU4W=Xu_6V&7qLHt*AhI09QH5_$WjCoRXIJr4pr3$!Z0;oI1FI8 z{t(Bb6u*C5<82;{8`i)9!T`oyxmJAvoU;~CywGE*FHf$wgn#%yt7=z zUIz1vC4EoL14&{$<0!MImmpvpyj>3HwdXYmNzDt(;bHJ^JJ%e^Cj(r>{z&ESQG9}y z_QkVLO2e4lZ@t7#hPyAj`v(RgtOoibPz5g2gPyaB{`q03ke$5QEBRAg+S8XwRsKoWxbeO1!R=S+MZPUBZI!aGcM>5 zqH93Cv=$^1D%JsO{wV_LPQZt7(P}M3Go@>RLOiVjYI(t7Y6W1^?khr7ZrY-atjkw3 z8p1KM@8m`^c%J!QFKJ?UC=^@bisNAl=3CkuI(g0_E#!x)5o8h0yIAEQgG>Ghc2H(( zbCcao!}C~9iQ%CvC+1b<5$y)6$rxgqTZh}21L9e@oLB*d?rpruB)P6h+grt~go@P= z}Y<#X0W9bH}nTZnhkiIX< i7MK*QyGk*Dd;p@GPcz*Cxv2ce(Hnxcx{Rp=cKCle{v~h# literal 68994 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 From 2f92245172c5e13a35791acf43405a7c162d873b Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Wed, 28 Oct 2020 10:06:37 +0800 Subject: [PATCH 10/12] Restore the whitespaces --- .../vendoring/patches/patched/piptools.patch | 138 +++++++++--------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/tasks/vendoring/patches/patched/piptools.patch b/tasks/vendoring/patches/patched/piptools.patch index b1694a6c46..d4bda87bd4 100644 --- a/tasks/vendoring/patches/patched/piptools.patch +++ b/tasks/vendoring/patches/patched/piptools.patch @@ -3,9 +3,9 @@ index fda80d5..4f7efbf 100644 --- a/pipenv/patched/piptools/_compat/__init__.py +++ b/pipenv/patched/piptools/_compat/__init__.py @@ -4,7 +4,37 @@ from __future__ import absolute_import, division, print_function, unicode_litera - + import six - + -from .pip_compat import PIP_VERSION, parse_requirements +from .pip_compat import ( + DEV_PKGS, @@ -38,7 +38,7 @@ index fda80d5..4f7efbf 100644 + user_cache_dir, + normalize_path, +) - + if six.PY2: from .tempfile import TemporaryDirectory diff --git a/pipenv/patched/piptools/_compat/pip_compat.py b/pipenv/patched/piptools/_compat/pip_compat.py @@ -54,19 +54,19 @@ index 9508b75..ea51421 100644 +os.environ["PIP_SHIMS_BASE_MODULE"] = str("pipenv.patched.notpip") +import pip_shims.shims +from pip_shims.models import ShimmedPathCollection, ImportTypes - + -import pip -from pip._internal.req import parse_requirements as _parse_requirements -from pip._vendor.packaging.version import parse as parse_version +InstallationCandidate = ShimmedPathCollection("InstallationCandidate", ImportTypes.CLASS) +InstallationCandidate.create_path("models.candidate", "18.0", "9999") +InstallationCandidate.create_path("index", "7.0.3", "10.9.9") - + -PIP_VERSION = tuple(map(int, parse_version(pip.__version__).base_version.split("."))) +PIP_VERSION = tuple(map(int, pip_shims.shims.parsed_pip_version.parsed_version.base_version.split("."))) - + +RequirementTracker = pip_shims.shims.RequirementTracker - + -if PIP_VERSION[:2] <= (20, 0): +def do_import(module_path, subimport=None, old_path=None): + old_path = old_path or module_path @@ -86,16 +86,16 @@ index 9508b75..ea51421 100644 + continue + else: + return getattr(imported, package) - + +if PIP_VERSION[:2] <= (20, 0): def install_req_from_parsed_requirement(req, **kwargs): return req - + - else: - from pip._internal.req.constructors import install_req_from_parsed_requirement + from pipenv.patched.notpip._internal.req.constructors import install_req_from_parsed_requirement - + +InstallRequirement = pip_shims.shims.InstallRequirement +InstallationError = pip_shims.shims.InstallationError +parse_requirements = pip_shims.shims.parse_requirements @@ -122,7 +122,7 @@ index 9508b75..ea51421 100644 +normalize_path = do_import("utils.misc", "normalize_path") +install_req_from_line = pip_shims.shims.install_req_from_line +install_req_from_editable = pip_shims.shims.install_req_from_editable - + def parse_requirements( filename, session, finder=None, options=None, constraint=False, isolated=False diff --git a/pipenv/patched/piptools/cache.py b/pipenv/patched/piptools/cache.py @@ -132,10 +132,10 @@ index 9b6bf55..983ddb6 100644 @@ -6,7 +6,7 @@ import os import platform import sys - + -from pip._vendor.packaging.requirements import Requirement +from pipenv.vendor.packaging.requirements import Requirement - + from .exceptions import PipToolsError from .utils import as_tuple, key_from_req, lookup_table diff --git a/pipenv/patched/piptools/locations.py b/pipenv/patched/piptools/locations.py @@ -145,12 +145,12 @@ index 9ca0ffe..36cc538 100644 @@ -1,12 +1,15 @@ import os from shutil import rmtree - + -from pip._internal.utils.appdirs import user_cache_dir +from ._compat import user_cache_dir - + from .click import secho - + -# The user_cache_dir helper comes straight from pip itself -CACHE_DIR = user_cache_dir("pip-tools") +# The user_cache_dir helper comes straight from pipenv.patched.notpip itself @@ -158,7 +158,7 @@ index 9ca0ffe..36cc538 100644 + from pipenv.environments import PIPENV_CACHE_DIR as CACHE_DIR +except ImportError: + CACHE_DIR = user_cache_dir("pipenv") - + # NOTE # We used to store the cache dir under ~/.pip-tools, which is not the diff --git a/pipenv/patched/piptools/repositories/local.py b/pipenv/patched/piptools/repositories/local.py @@ -166,15 +166,15 @@ index ec3a796..1aa29f0 100644 --- a/pipenv/patched/piptools/repositories/local.py +++ b/pipenv/patched/piptools/repositories/local.py @@ -3,9 +3,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera - + from contextlib import contextmanager - + -from pip._internal.utils.hashes import FAVORITE_HASH - -from .._compat import PIP_VERSION +from .._compat import PIP_VERSION, FAVORITE_HASH from .base import BaseRepository - + from piptools.utils import as_tuple, key_from_ireq, make_install_requirement @@ -65,7 +63,8 @@ class LocalRequirementsRepository(BaseRepository): if existing_pin and ireq_satisfied_by_existing_pin(ireq, existing_pin): @@ -192,14 +192,14 @@ index ef5ba4e..8f74271 100644 +++ b/pipenv/patched/piptools/repositories/pypi.py @@ -2,28 +2,48 @@ from __future__ import absolute_import, division, print_function, unicode_literals - + import collections +import copy import hashlib import os from contextlib import contextmanager from shutil import rmtree - + -from pip._internal.cache import WheelCache -from pip._internal.commands import create_command -from pip._internal.models.index import PyPI @@ -255,12 +255,12 @@ index ef5ba4e..8f74271 100644 @@ -32,10 +52,53 @@ from ..utils import ( ) from .base import BaseRepository - + +os.environ["PIP_SHIMS_BASE_MODULE"] = str("pipenv.patched.notpip") FILE_CHUNK_SIZE = 4096 FileStream = collections.namedtuple("FileStream", "stream size") - - + + +class HashCache(SafeFileCache): + """Caches hashes of PyPI artifacts so we do not need to re-download them + @@ -305,11 +305,11 @@ index ef5ba4e..8f74271 100644 + class PyPIRepository(BaseRepository): DEFAULT_INDEX_URL = PyPI.simple_url - + @@ -46,21 +109,29 @@ class PyPIRepository(BaseRepository): changed/configured on the Finder. """ - + - def __init__(self, pip_args, cache_dir): + def __init__(self, pip_args, cache_dir=CACHE_DIR, session=None, build_isolation=False, use_json=False): + self.build_isolation = build_isolation @@ -326,10 +326,10 @@ index ef5ba4e..8f74271 100644 + self.options.build_isolation = build_isolation if self.options.cache_dir: self.options.cache_dir = normalize_path(self.options.cache_dir) - + self.options.require_hashes = False self.options.ignore_dependencies = False - + - self.session = self.command._build_session(self.options) + if session is None: + session = self.command._build_session(self.options) @@ -338,7 +338,7 @@ index ef5ba4e..8f74271 100644 - options=self.options, session=self.session + options=self.options, session=self.session, ignore_requires_python=True ) - + # Caches @@ -73,6 +144,10 @@ class PyPIRepository(BaseRepository): # of all secondary dependencies for the given requirement, so we @@ -348,13 +348,13 @@ index ef5ba4e..8f74271 100644 + + # stores *full* path + fragment => sha256 + self._hash_cache = HashCache(session=session) - + # Setup file paths self.freshen_build_caches() @@ -114,13 +189,15 @@ class PyPIRepository(BaseRepository): if ireq.editable or is_url_requirement(ireq): return ireq # return itself as the best match - + - all_candidates = self.find_all_candidates(ireq.name) + all_candidates = clean_requires_python(self.find_all_candidates(ireq.name)) candidates_by_version = lookup_table( @@ -368,7 +368,7 @@ index ef5ba4e..8f74271 100644 + prereleases=prereleases) + except TypeError: + matching_versions = [candidate.version for candidate in all_candidates] - + # Reuses pip's internal candidate sort key to sort matching_candidates = [candidates_by_version[ver] for ver in matching_versions] @@ -136,9 +213,66 @@ class PyPIRepository(BaseRepository): @@ -378,7 +378,7 @@ index ef5ba4e..8f74271 100644 + ireq.markers, constraint=ireq.constraint, ) - + + def get_dependencies(self, ireq): + json_results = set() + @@ -448,14 +448,14 @@ index ef5ba4e..8f74271 100644 upgrade_strategy="to-satisfy-only", ) @@ -173,10 +307,11 @@ class PyPIRepository(BaseRepository): - + if PIP_VERSION[:2] <= (20, 0): reqset.cleanup_files() + results = set(results) if results else set() - + - return set(results) + return results, ireq - + - def get_dependencies(self, ireq): + def get_legacy_dependencies(self, ireq): """ @@ -479,7 +479,7 @@ index ef5ba4e..8f74271 100644 cached_link = link - return {self._get_file_hash(cached_link)} + return {self._hash_cache._get_file_hash(cached_link)} - + if not is_pinned_requirement(ireq): raise TypeError("Expected pinned requirement, got {}".format(ireq)) @@ -260,38 +396,28 @@ class PyPIRepository(BaseRepository): @@ -494,7 +494,7 @@ index ef5ba4e..8f74271 100644 - matching_candidates = candidates_by_version[matching_versions[0]] - - log.debug(" {}".format(ireq.name)) - + - return { - self._get_file_hash(candidate.link) for candidate in matching_candidates - } @@ -525,7 +525,7 @@ index ef5ba4e..8f74271 100644 + if h is not None + } + return result - + - # Iterate over the chosen context manager - with context_manager as bar: - for chunk in bar: @@ -538,7 +538,7 @@ index ef5ba4e..8f74271 100644 + yield + finally: + self.finder._ignore_compatibility = False - + @contextmanager def allow_all_wheels(self): diff --git a/pipenv/patched/piptools/resolver.py b/pipenv/patched/piptools/resolver.py @@ -548,12 +548,12 @@ index 0116992..550069d 100644 @@ -6,7 +6,9 @@ import os from functools import partial from itertools import chain, count - + -from pip._internal.req.constructors import install_req_from_line +from pip_shims.shims import install_req_from_line +from pipenv.vendor.requirementslib.models.markers import normalize_marker_str +from packaging.markers import Marker - + from . import click from .logging import log @@ -33,6 +35,7 @@ class RequirementSummary(object): @@ -562,7 +562,7 @@ index 0116992..550069d 100644 self.extras = str(sorted(ireq.extras)) + self.markers = ireq.markers self.specifier = str(ireq.specifier) - + def __eq__(self, other): @@ -63,6 +66,17 @@ def combine_install_requirements(ireqs): if combined_ireq.req is not None and ireq.req is not None: @@ -585,7 +585,7 @@ index 0116992..550069d 100644 @@ -337,10 +351,19 @@ class Resolver(object): if ireq.constraint: return - + - if ireq.editable or is_url_requirement(ireq): + if ireq.editable or (is_url_requirement(ireq) and not ireq.link.is_wheel): for dependency in self.repository.get_dependencies(ireq): @@ -609,12 +609,12 @@ index 0116992..550069d 100644 dependencies = self.repository.get_dependencies(ireq) - self.dependency_cache[ireq] = sorted(str(ireq.req) for ireq in dependencies) + self.dependency_cache[ireq] = sorted(set(format_requirement(ireq) for ireq in dependencies)) - + # Example: ['Werkzeug>=0.9', 'Jinja2>=2.4'] dependency_strings = self.dependency_cache[ireq] @@ -374,7 +397,8 @@ class Resolver(object): ) - + def reverse_dependencies(self, ireqs): + is_non_wheel_url = lambda r: is_url_requirement(r) and not r.link.is_wheel non_editable = [ @@ -628,26 +628,26 @@ index 03232a8..f83b13e 100755 +++ b/pipenv/patched/piptools/scripts/compile.py @@ -7,8 +7,8 @@ import sys import tempfile - + from click.utils import safecall -from pip._internal.commands import create_command -from pip._internal.req.constructors import install_req_from_line +from ._compat import InstallCommand +from ._compat import install_req_from_line - + from .. import click from .._compat import parse_requirements @@ -24,8 +24,8 @@ from ..writer import OutputWriter DEFAULT_REQUIREMENTS_FILE = "requirements.in" DEFAULT_REQUIREMENTS_OUTPUT_FILE = "requirements.txt" - + -# Get default values of the pip's options (including options from pip.conf). -install_command = create_command("install") +# Get default values of the pip's options (including options from pipenv.patched.notpip.conf). +install_command = InstallComand() pip_defaults = install_command.parser.get_default_values() - - + + diff --git a/pipenv/patched/piptools/scripts/sync.py b/pipenv/patched/piptools/scripts/sync.py index 137e813..4a7b3d5 100755 --- a/pipenv/patched/piptools/scripts/sync.py @@ -655,17 +655,17 @@ index 137e813..4a7b3d5 100755 @@ -6,8 +6,7 @@ import os import shlex import sys - + -from pip._internal.commands import create_command -from pip._internal.utils.misc import get_installed_distributions +from ._compat import get_installed_distributions, InstallCommand - + from .. import click, sync from .._compat import parse_requirements @@ -112,7 +111,7 @@ def cli( log.error("ERROR: " + msg) sys.exit(2) - + - install_command = create_command("install") + install_command = InstallCommand() options, _ = install_command.parse_args([]) @@ -678,12 +678,12 @@ index 430b4bb..015ff7a 100644 @@ -4,8 +4,8 @@ import sys import tempfile from subprocess import check_call # nosec - + -from pip._internal.commands.freeze import DEV_PKGS -from pip._internal.utils.compat import stdlib_pkgs +from ._compat import DEV_PKGS +from ._compat import stdlib_pkgs - + from . import click from .exceptions import IncompatibleRequirements diff --git a/pipenv/patched/piptools/utils.py b/pipenv/patched/piptools/utils.py @@ -693,12 +693,12 @@ index 7733447..1123fb6 100644 @@ -1,14 +1,19 @@ # coding: utf-8 from __future__ import absolute_import, division, print_function, unicode_literals - + +import os import sys from collections import OrderedDict from itertools import chain, groupby - + import six from click.utils import LazyFile -from pip._internal.req.constructors import install_req_from_line @@ -708,13 +708,13 @@ index 7733447..1123fb6 100644 +from pipenv.vendor.packaging.version import Version, InvalidVersion, parse as parse_version +from pipenv.vendor.packaging.markers import Marker, Op, Value, Variable + - + from ._compat import PIP_VERSION from .click import style @@ -25,6 +30,70 @@ COMPILE_EXCLUDE_OPTIONS = { } - - + + +def simplify_markers(ireq): + """simplify_markers "This code cleans up markers for a specific :class:`~InstallRequirement`" + @@ -784,8 +784,8 @@ index 7733447..1123fb6 100644 if ireq.req is None and ireq.link is not None: @@ -50,16 +119,51 @@ def comment(text): return style(text, fg="green") - - + + -def make_install_requirement(name, version, extras, constraint=False): +def make_install_requirement(name, version, extras, markers, constraint=False): # If no extras are specified, the extras string is blank @@ -793,7 +793,7 @@ index 7733447..1123fb6 100644 if extras: # Sort extras for stability extras_string = "[{}]".format(",".join(sorted(extras))) - + - return install_req_from_line( - str("{}{}=={}".format(name, extras_string, version)), constraint=constraint - ) @@ -835,8 +835,8 @@ index 7733447..1123fb6 100644 + parts.append("; {0}".format(requirement.marker)) + + return "".join(parts) - - + + def is_url_requirement(ireq): @@ -77,13 +181,15 @@ def format_requirement(ireq, marker=None, hashes=None): """ @@ -849,11 +849,11 @@ index 7733447..1123fb6 100644 else: - line = str(ireq.req).lower() + line = _requirement_to_str_lowercase_name(ireq.req) - + - if marker: - line = "{} ; {}".format(line, marker) + if marker and ';' not in line: + line = "{}; {}".format(line, marker) - + if hashes: for hash_ in sorted(hashes): From 98fbe2fc5ae67430ff6879b2968cbeae29146fbc Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Wed, 28 Oct 2020 10:44:02 +0800 Subject: [PATCH 11/12] Supply sha256 checksum to mocked PyPI --- tests/pypi | 2 +- tests/pytest-pypi/pytest_pypi/app.py | 8 ++++++-- tests/pytest-pypi/pytest_pypi/templates/package.html | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/pypi b/tests/pypi index 1881ecb454..77faecd45e 160000 --- a/tests/pypi +++ b/tests/pypi @@ -1 +1 @@ -Subproject commit 1881ecb45431952d2e18e2be3416a8835e53778a +Subproject commit 77faecd45e85b14448667bc16af32ddfe6f8d42d diff --git a/tests/pytest-pypi/pytest_pypi/app.py b/tests/pytest-pypi/pytest_pypi/app.py index f1812af3bd..151c26b474 100644 --- a/tests/pytest-pypi/pytest_pypi/app.py +++ b/tests/pytest-pypi/pytest_pypi/app.py @@ -16,7 +16,7 @@ from flask import Flask, redirect, abort, render_template, send_file, jsonify -ReleaseTuple = collections.namedtuple("ReleaseTuple", ["path", "requires_python"]) +ReleaseTuple = collections.namedtuple("ReleaseTuple", ["path", "requires_python", "hash"]) app = Flask(__name__) session = requests.Session() @@ -86,13 +86,17 @@ def add_release(self, path_to_binary): path_to_binary = os.path.abspath(path_to_binary) path, release = os.path.split(path_to_binary) requires_python = "" + hash_value = "" if path_to_binary.endswith(".whl"): pkg = distlib.wheel.Wheel(path_to_binary) md_dict = pkg.metadata.todict() requires_python = md_dict.get("requires_python", "") if requires_python.count(".") > 1: requires_python, _, _ = requires_python.rpartition(".") - self.releases[release] = ReleaseTuple(path_to_binary, requires_python) + if os.path.isfile(path_to_binary + ".sha256"): + with open(path_to_binary + ".sha256") as f: + hash_value = f.read().strip() + self.releases[release] = ReleaseTuple(path_to_binary, requires_python, hash_value) self._package_dirs.add(ReleaseTuple(path, requires_python)) diff --git a/tests/pytest-pypi/pytest_pypi/templates/package.html b/tests/pytest-pypi/pytest_pypi/templates/package.html index 3d3645177f..f8c5858a64 100644 --- a/tests/pytest-pypi/pytest_pypi/templates/package.html +++ b/tests/pytest-pypi/pytest_pypi/templates/package.html @@ -7,7 +7,7 @@

Links for {{ package.name }}

{% for release, value in package.releases.items() %} - {{ release }} + {{ release }}
{% endfor %} From 4f4de7bcfe59a67fc468068c1ce6cb14ace5c6a0 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Wed, 28 Oct 2020 10:50:02 +0800 Subject: [PATCH 12/12] Fix release tuple construction --- tests/pytest-pypi/pytest_pypi/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pytest-pypi/pytest_pypi/app.py b/tests/pytest-pypi/pytest_pypi/app.py index 151c26b474..96bb774eb7 100644 --- a/tests/pytest-pypi/pytest_pypi/app.py +++ b/tests/pytest-pypi/pytest_pypi/app.py @@ -97,7 +97,7 @@ def add_release(self, path_to_binary): with open(path_to_binary + ".sha256") as f: hash_value = f.read().strip() self.releases[release] = ReleaseTuple(path_to_binary, requires_python, hash_value) - self._package_dirs.add(ReleaseTuple(path, requires_python)) + self._package_dirs.add(ReleaseTuple(path, requires_python, hash_value)) class Artifact(object):