Merge pull request #2267 from pypa/feature/better-resolver

Feature/better resolver
This commit is contained in:
Tzu-ping Chung
2018-05-28 17:26:53 +09:00
committed by GitHub
7 changed files with 259 additions and 121 deletions
+5 -2
View File
@@ -29,6 +29,11 @@ except ImportError:
_types.add(type(arg))
return _types.pop()
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
try:
from weakref import finalize
@@ -51,8 +56,6 @@ if six.PY2:
class ResourceWarning(Warning):
pass
# -*- coding=utf-8 -*-
def pip_import(module_path, subimport=None, old_path=None):
internal = 'pip._internal.{0}'.format(module_path)
+19 -4
View File
@@ -46,10 +46,12 @@ from .utils import (
rmtree,
split_argument,
extract_uri_from_vcs_dep,
fs_str,
)
from ._compat import (
TemporaryDirectory,
vcs
vcs,
Path
)
from .import pep508checker, progress
from .environments import (
@@ -73,6 +75,7 @@ from .environments import (
PIPENV_SHELL,
PIPENV_PYTHON,
PIPENV_VIRTUALENV,
PIPENV_CACHE_DIR,
)
# Backport required for earlier versions of Python.
@@ -1481,7 +1484,7 @@ def pip_install(
pre = '--pre' if pre else ''
quoted_pip = which_pip(allow_global=allow_global)
quoted_pip = escape_grouped_arguments(quoted_pip)
upgrade_strategy = '--upgrade --upgrade-strategy=only-if-needed' if selective_upgrade else ''
upgrade_strategy = '--upgrade --upgrade-strategy=to-satisfy-only' if selective_upgrade else ''
pip_command = '{0} install {4} {5} {6} {7} {3} {1} {2} --exists-action w'.format(
quoted_pip,
install_reqs,
@@ -1494,11 +1497,23 @@ def pip_install(
)
if verbose:
click.echo('$ {0}'.format(pip_command), err=True)
c = delegator.run(pip_command, block=block)
cache_dir = Path(PIPENV_CACHE_DIR)
pip_config = {
'PIP_CACHE_DIR': fs_str(cache_dir.as_posix()),
'PIP_WHEEL_DIR': fs_str(cache_dir.joinpath('wheels').as_posix()),
'PIP_DESTINATION_DIR': fs_str(cache_dir.joinpath('pkgs').as_posix()),
}
c = delegator.run(pip_command, block=block, env=pip_config)
return c
def pip_download(package_name):
cache_dir = Path(PIPENV_CACHE_DIR)
pip_config = {
'PIP_CACHE_DIR': fs_str(cache_dir.as_posix()),
'PIP_WHEEL_DIR': fs_str(cache_dir.joinpath('wheels').as_posix()),
'PIP_DESTINATION_DIR': fs_str(cache_dir.joinpath('pkgs').as_posix()),
}
for source in project.sources:
cmd = '{0} download "{1}" -i {2} -d {3}'.format(
escape_grouped_arguments(which_pip()),
@@ -1506,7 +1521,7 @@ def pip_download(package_name):
source['url'],
project.download_location,
)
c = delegator.run(cmd)
c = delegator.run(cmd, env=pip_config)
if c.return_code == 0:
break
+45 -18
View File
@@ -4,6 +4,7 @@ from __future__ import (absolute_import, division, print_function,
import hashlib
import os
import sys
from contextlib import contextmanager
from shutil import rmtree
@@ -20,14 +21,17 @@ from .._compat import (
SafeFileCache,
)
from notpip._vendor.packaging.requirements import InvalidRequirement
from notpip._vendor.packaging.requirements import InvalidRequirement, Requirement
from notpip._vendor.packaging.version import Version, InvalidVersion, parse as parse_version
from notpip._vendor.packaging.specifiers import SpecifierSet
from notpip._vendor.pyparsing import ParseException
from ..cache import CACHE_DIR
from pipenv.environments import PIPENV_CACHE_DIR
from ..exceptions import NoCandidateFound
from ..utils import (fs_str, is_pinned_requirement, lookup_table,
make_install_requirement)
from ..utils import (fs_str, is_pinned_requirement, lookup_table, as_tuple, key_from_req,
make_install_requirement, format_requirement, dedup)
from .base import BaseRepository
@@ -159,7 +163,13 @@ class PyPIRepository(BaseRepository):
if ireq.editable:
return ireq # return itself as the best match
all_candidates = self.find_all_candidates(ireq.name)
py_version = parse_version(os.environ.get('PIP_PYTHON_VERSION', str(sys.version_info[:3])))
all_candidates = []
for c in self.find_all_candidates(ireq.name):
if c.requires_python and not SpecifierSet(c.requires_python).contains(py_version):
continue
all_candidates.append(c)
candidates_by_version = lookup_table(all_candidates, key=lambda c: c.version, unique=True)
try:
matching_versions = ireq.specifier.filter((candidate.version for candidate in all_candidates),
@@ -188,21 +198,33 @@ class PyPIRepository(BaseRepository):
raise TypeError('Expected pinned InstallRequirement, got {}'.format(ireq))
def gen(ireq):
if self.DEFAULT_INDEX_URL in self.finder.index_urls:
if self.DEFAULT_INDEX_URL not in self.finder.index_urls:
return
url = 'https://pypi.org/pypi/{0}/json'.format(ireq.req.name)
r = self.session.get(url)
url = 'https://pypi.org/pypi/{0}/json'.format(ireq.req.name)
releases = self.session.get(url).json()['releases']
# TODO: Latest isn't always latest.
latest = list(r.json()['releases'].keys())[-1]
if str(ireq.req.specifier) == '=={0}'.format(latest):
latest_url = 'https://pypi.org/pypi/{0}/{1}/json'.format(ireq.req.name, latest)
latest_requires = self.session.get(latest_url)
for requires in latest_requires.json().get('info', {}).get('requires_dist', {}):
i = InstallRequirement.from_line(requires)
matches = [
r for r in releases
if '=={0}'.format(r) == str(ireq.req.specifier)
]
if not matches:
return
if 'extra' not in repr(i.markers):
yield i
release_requires = self.session.get(
'https://pypi.org/pypi/{0}/{1}/json'.format(
ireq.req.name, matches[0],
),
).json()
try:
requires_dist = release_requires['info']['requires_dist']
except KeyError:
return
for requires in requires_dist:
i = InstallRequirement.from_line(requires)
if 'extra' not in repr(i.markers):
yield i
try:
if ireq not in self._json_dep_cache:
@@ -226,7 +248,6 @@ class PyPIRepository(BaseRepository):
return json_results
def get_legacy_dependencies(self, ireq):
"""
Given a pinned or an editable InstallRequirement, returns a set of
@@ -245,7 +266,13 @@ class PyPIRepository(BaseRepository):
setup_requires = self.finder.get_extras_links(
dist.get_metadata_lines('requires.txt')
)
except TypeError:
# HACK: Sometimes the InstallRequirement doesn't properly get
# these values set on it during the resolution process. It's
# difficult to pin down what is going wrong. This fixes things.
ireq.version = dist.version
ireq.project_name = dist.project_name
ireq.req = dist.as_requirement()
except (TypeError, ValueError):
pass
if ireq not in self._dependencies_cache:
+3 -3
View File
@@ -20,7 +20,7 @@ except ImportError:
from pathlib2 import Path
from .cmdparse import Script
from .vendor.requirementslib import Requirement
from .vendor.requirementslib.requirements import Requirement
from .utils import (
atomic_open_for_write,
mkdir_p,
@@ -728,8 +728,8 @@ class Project(object):
# Read and append Pipfile.
p = self.parsed_pipfile
# Don't re-capitalize file URLs or VCSs.
package = Requirement.from_line(package_name)
converted = first(package.as_pipfile().values())
package = Requirement.from_line(package_name.strip())
_, converted = package.pipfile_entry
key = 'dev-packages' if dev else 'packages'
# Set empty group if it doesn't exist yet.
if key not in p:
+112 -64
View File
@@ -1,20 +1,20 @@
# -*- coding: utf-8 -*-
import errno
import logging
import os
import re
import hashlib
import tempfile
import sys
import shutil
import logging
import sys
import crayons
import parse
import six
import stat
import warnings
from click import echo as click_echo
from click import echo as click_echo
from first import first
try:
from weakref import finalize
except ImportError:
@@ -28,9 +28,10 @@ except ImportError:
def detach(self):
return False
logging.basicConfig(level=logging.ERROR)
from time import time
logging.basicConfig(level=logging.ERROR)
try:
from urllib.parse import urlparse
except ImportError:
@@ -48,7 +49,7 @@ from .pep508checker import lookup
from .environments import (
PIPENV_MAX_ROUNDS,
PIPENV_CACHE_DIR,
PIPENV_MAX_RETRIES
PIPENV_MAX_RETRIES,
)
try:
@@ -219,8 +220,8 @@ def prepare_pip_source_args(sources, pip_args=None):
def actually_resolve_reps(
deps, index_lookup, markers_lookup, project, sources, verbose, clear, pre, req_dir=None
):
from .patched.notpip._internal import basecommand, req
from .patched.notpip._vendor import requests as pip_requests
from .patched.notpip._internal import basecommand
from .patched.notpip._internal.req import parse_requirements
from .patched.notpip._internal.exceptions import DistributionNotFound
from .patched.notpip._vendor.requests.exceptions import HTTPError
from pipenv.patched.piptools.resolver import Resolver
@@ -228,7 +229,7 @@ def actually_resolve_reps(
from pipenv.patched.piptools.scripts.compile import get_pip_command
from pipenv.patched.piptools import logging as piptools_logging
from pipenv.patched.piptools.exceptions import NoCandidateFound
from ._compat import TemporaryDirectory
from ._compat import TemporaryDirectory, NamedTemporaryFile
class PipCommand(basecommand.Command):
"""Needed for pip-tools."""
@@ -240,54 +241,51 @@ def actually_resolve_reps(
req_dir = TemporaryDirectory(suffix='-requirements', prefix='pipenv-')
cleanup_req_dir = True
for dep in deps:
if dep:
if dep.startswith('-e '):
constraint = req.InstallRequirement.from_editable(
dep[len('-e '):]
)
else:
fd, t = tempfile.mkstemp(
prefix='pipenv-', suffix='-requirement.txt', dir=req_dir.name
)
with os.fdopen(fd, 'w') as f:
f.write(dep)
constraint = [
c for c in req.parse_requirements(t, session=pip_requests)
][
0
]
# extra_constraints = []
if ' -i ' in dep:
index_lookup[constraint.name] = project.get_source(
url=dep.split(' -i ')[1]
).get(
'name'
)
if constraint.markers:
markers_lookup[constraint.name] = str(
constraint.markers
).replace(
'"', "'"
)
constraints.append(constraint)
if not dep:
continue
url = None
if ' -i ' in dep:
dep, url = dep.split(' -i ')
req = Requirement.from_line(dep)
# req.as_line() is theoratically the same as dep, but is guarenteed to
# be normalized. This is safer than passing in dep.
# TODO: Stop passing dep lines around; just use requirement objects.
constraints.append(req.as_line())
# extra_constraints = []
if url:
index_lookup[req.name] = project.get_source(url=url).get('name')
if req.markers:
markers_lookup[req.name] = req.markers.replace('"', "'")
constraints_file = None
pip_command = get_pip_command()
pip_args = []
if sources:
pip_args = prepare_pip_source_args(sources, pip_args)
with NamedTemporaryFile(mode='w', prefix='pipenv-', suffix='-constraints.txt', dir=req_dir.name, delete=False) as f:
if sources:
requirementstxt_sources = ' '.join(pip_args).replace(' --', '\n--')
f.write(u'{0}\n'.format(requirementstxt_sources))
f.write(u'\n'.join([_constraint for _constraint in constraints]))
constraints_file = f.name
if verbose:
print('Using pip: {0}'.format(' '.join(pip_args)))
pip_args = pip_args.extend(['--cache-dir', PIPENV_CACHE_DIR])
pip_options, _ = pip_command.parse_args(pip_args)
session = pip_command._build_session(pip_options)
pypi = PyPIRepository(
pip_options=pip_options, use_json=False, session=session
pip_options=pip_options, use_json=True, session=session
)
if verbose:
logging.log.verbose = True
piptools_logging.log.verbose = True
resolved_tree = set()
resolver = Resolver(
constraints=constraints,
constraints=parse_requirements(
constraints_file,
finder=pypi.finder, session=pypi.session, options=pip_options,
),
repository=pypi,
clear_caches=clear,
prereleases=pre,
@@ -1130,7 +1128,7 @@ def extract_uri_from_vcs_dep(dep):
return None
def install_or_update_vcs(vcs_obj, src_dir, name, rev=None):
def install_or_update_vcs(vcs_obj, src_dir, name, rev=None):
target_dir = os.path.join(src_dir, name)
target_rev = vcs_obj.make_rev_options(rev)
if not os.path.exists(target_dir):
@@ -1139,50 +1137,100 @@ def install_or_update_vcs(vcs_obj, src_dir, name, rev=None):
return vcs_obj.get_revision(target_dir)
def get_vcs_deps(project, pip_freeze=None, which=None, verbose=False, clear=False, pre=False, allow_global=False, dev=False):
from ._compat import vcs
section = 'vcs_dev_packages' if dev else 'vcs_packages'
def get_vcs_deps(
project,
pip_freeze=None,
which=None,
verbose=False,
clear=False,
pre=False,
allow_global=False,
dev=False,
):
from .patched.notpip._internal.vcs import VcsSupport
section = "vcs_dev_packages" if dev else "vcs_packages"
lines = []
lockfiles = []
try:
packages = getattr(project, section)
except AttributeError:
return [], []
vcs_registry = vcs()
src_dir = Path(
os.environ.get("PIP_SRC", os.path.join(project.virtualenv_location, "src"))
)
src_dir.mkdir(mode=0o775, exist_ok=True)
vcs_registry = VcsSupport
vcs_uri_map = {
extract_uri_from_vcs_dep(v): {'name': k, 'ref': v.get('ref')}
extract_uri_from_vcs_dep(v): {"name": k, "ref": v.get("ref")}
for k, v in packages.items()
}
for line in pip_freeze.strip().split('\n'):
for line in pip_freeze.strip().split("\n"):
# if the line doesn't match a vcs dependency in the Pipfile,
# ignore it
_vcs_match = first(_uri for _uri in vcs_uri_map.keys() if _uri in line)
if not _vcs_match:
continue
pipfile_name = vcs_uri_map[_vcs_match]['name']
pipfile_rev = vcs_uri_map[_vcs_match]['ref']
src_dir = os.environ.get('PIP_SRC', os.path.join(project.virtualenv_location, 'src'))
mkdir_p(src_dir)
pipfile_name = vcs_uri_map[_vcs_match]["name"]
pipfile_rev = vcs_uri_map[_vcs_match]["ref"]
pipfile_req = Requirement.from_pipfile(pipfile_name, [], packages[pipfile_name])
names = {pipfile_name}
_pip_uri = line.lstrip('-e ')
backend_name = str(_pip_uri.split('+', 1)[0])
backend = vcs_registry._registry[first(b for b in vcs_registry if b == backend_name)]
__vcs = backend(url=_pip_uri)
backend = vcs_registry()._registry.get(pipfile_req.vcs)
# TODO: Why doesn't pip freeze list 'git+git://' formatted urls?
if line.startswith("-e ") and not "{0}+".format(pipfile_req.vcs) in line:
line = line.replace("-e ", "-e {0}+".format(pipfile_req.vcs))
installed = Requirement.from_line(line)
__vcs = backend(url=installed.req.uri)
names.add(installed.normalized_name)
locked_rev = None
for _name in names:
locked_rev = install_or_update_vcs(__vcs, src_dir, _name, rev=pipfile_rev)
locked_rev = install_or_update_vcs(
__vcs, src_dir.as_posix(), _name, rev=pipfile_rev
)
if installed.is_vcs:
installed.req.ref = locked_rev
lockfiles.append({pipfile_name: installed.pipfile_entry[1]})
pipfile_srcdir = os.path.join(src_dir, pipfile_name)
lockfile_srcdir = os.path.join(src_dir, installed.normalized_name)
pipfile_srcdir = (src_dir / pipfile_name).as_posix()
lockfile_srcdir = (src_dir / installed.normalized_name).as_posix()
lines.append(line)
if os.path.exists(pipfile_srcdir):
lockfiles.extend(venv_resolve_deps(['-e {0}'.format(pipfile_srcdir)], which=which, verbose=verbose, project=project, clear=clear, pre=pre, allow_global=allow_global))
lockfiles.extend(
venv_resolve_deps(
["-e {0}".format(pipfile_srcdir)],
which=which,
verbose=verbose,
project=project,
clear=clear,
pre=pre,
allow_global=allow_global,
)
)
else:
lockfiles.extend(venv_resolve_deps(['-e {0}'.format(lockfile_srcdir)], which=which, verbose=verbose, project=project, clear=clear, pre=pre, allow_global=allow_global))
lockfiles.extend(
venv_resolve_deps(
["-e {0}".format(lockfile_srcdir)],
which=which,
verbose=verbose,
project=project,
clear=clear,
pre=pre,
allow_global=allow_global,
)
)
return lines, lockfiles
def fs_str(string):
"""Encodes a string into the proper filesystem encoding
Borrowed from pip-tools
"""
if isinstance(string, str):
return string
assert not isinstance(string, bytes)
return string.encode(_fs_encoding)
_fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
+4 -1
View File
@@ -468,7 +468,10 @@ def generate_patch(ctx, package_path, patch_description, base='HEAD'):
pkg = Path(package_path)
if len(pkg.parts) != 2 or pkg.parts[0] not in ('vendor', 'patched'):
raise ValueError('example usage: generate-patch patched/pew some-description')
patch_fn = '{0}-{1}.patch'.format(pkg.parts[1], patch_description)
if patch_description:
patch_fn = '{0}-{1}.patch'.format(pkg.parts[1], patch_description)
else:
patch_fn = '{0}.patch'.format(pkg.parts[1])
command = 'git diff {base} -p {root} > {out}'.format(
base=base,
root=Path('pipenv').joinpath(pkg),
+71 -29
View File
@@ -19,10 +19,18 @@ index 4e6174c..75f9b49 100644
# NOTE
# We used to store the cache dir under ~/.pip-tools, which is not the
diff --git a/pipenv/patched/piptools/repositories/pypi.py b/pipenv/patched/piptools/repositories/pypi.py
index 1c4b943..8320e14 100644
index 1c4b943..aae0122 100644
--- a/pipenv/patched/piptools/repositories/pypi.py
+++ b/pipenv/patched/piptools/repositories/pypi.py
@@ -15,10 +15,16 @@ from .._compat import (
@@ -4,6 +4,7 @@ from __future__ import (absolute_import, division, print_function,
import hashlib
import os
+import sys
from contextlib import contextmanager
from shutil import rmtree
@@ -15,13 +16,22 @@ from .._compat import (
Wheel,
FAVORITE_HASH,
TemporaryDirectory,
@@ -32,15 +40,23 @@ index 1c4b943..8320e14 100644
+ SafeFileCache,
)
+from pip._vendor.packaging.requirements import InvalidRequirement
+from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
+from pip._vendor.packaging.version import Version, InvalidVersion, parse as parse_version
+from pip._vendor.packaging.specifiers import SpecifierSet
+from pip._vendor.pyparsing import ParseException
+
from ..cache import CACHE_DIR
+from pipenv.environments import PIPENV_CACHE_DIR
from ..exceptions import NoCandidateFound
from ..utils import (fs_str, is_pinned_requirement, lookup_table,
make_install_requirement)
@@ -37,6 +43,40 @@ except ImportError:
-from ..utils import (fs_str, is_pinned_requirement, lookup_table,
- make_install_requirement)
+from ..utils import (fs_str, is_pinned_requirement, lookup_table, as_tuple, key_from_req,
+ make_install_requirement, format_requirement, dedup)
+
from .base import BaseRepository
@@ -37,6 +47,40 @@ except ImportError:
from pip.wheel import WheelCache
@@ -81,7 +97,7 @@ index 1c4b943..8320e14 100644
class PyPIRepository(BaseRepository):
DEFAULT_INDEX_URL = PyPI.simple_url
@@ -46,10 +86,11 @@ class PyPIRepository(BaseRepository):
@@ -46,10 +90,11 @@ class PyPIRepository(BaseRepository):
config), but any other PyPI mirror can be used if index_urls is
changed/configured on the Finder.
"""
@@ -95,7 +111,7 @@ index 1c4b943..8320e14 100644
index_urls = [pip_options.index_url] + pip_options.extra_index_urls
if pip_options.no_index:
@@ -74,11 +115,15 @@ class PyPIRepository(BaseRepository):
@@ -74,11 +119,15 @@ class PyPIRepository(BaseRepository):
# of all secondary dependencies for the given requirement, so we
# only have to go to disk once for each requirement
self._dependencies_cache = {}
@@ -113,9 +129,18 @@ index 1c4b943..8320e14 100644
def freshen_build_caches(self):
"""
@@ -116,8 +161,11 @@ class PyPIRepository(BaseRepository):
@@ -114,10 +163,19 @@ class PyPIRepository(BaseRepository):
if ireq.editable:
return ireq # return itself as the best match
all_candidates = self.find_all_candidates(ireq.name)
- all_candidates = self.find_all_candidates(ireq.name)
+ py_version = parse_version(os.environ.get('PIP_PYTHON_VERSION', str(sys.version_info[:3])))
+ all_candidates = []
+ for c in self.find_all_candidates(ireq.name):
+ if c.requires_python and not SpecifierSet(c.requires_python).contains(py_version):
+ continue
+ all_candidates.append(c)
+
candidates_by_version = lookup_table(all_candidates, key=lambda c: c.version, unique=True)
- matching_versions = ireq.specifier.filter((candidate.version for candidate in all_candidates),
+ try:
@@ -126,7 +151,7 @@ index 1c4b943..8320e14 100644
# Reuses pip's internal candidate sort key to sort
matching_candidates = [candidates_by_version[ver] for ver in matching_versions]
@@ -126,11 +174,60 @@ class PyPIRepository(BaseRepository):
@@ -126,11 +184,71 @@ class PyPIRepository(BaseRepository):
best_candidate = max(matching_candidates, key=self.finder._candidate_sort_key)
# Turn the candidate into a pinned InstallRequirement
@@ -147,21 +172,33 @@ index 1c4b943..8320e14 100644
+ raise TypeError('Expected pinned InstallRequirement, got {}'.format(ireq))
+
+ def gen(ireq):
+ if self.DEFAULT_INDEX_URL in self.finder.index_urls:
+ if self.DEFAULT_INDEX_URL not in self.finder.index_urls:
+ return
+
+ url = 'https://pypi.org/pypi/{0}/json'.format(ireq.req.name)
+ r = self.session.get(url)
+ url = 'https://pypi.org/pypi/{0}/json'.format(ireq.req.name)
+ releases = self.session.get(url).json()['releases']
+
+ # TODO: Latest isn't always latest.
+ latest = list(r.json()['releases'].keys())[-1]
+ if str(ireq.req.specifier) == '=={0}'.format(latest):
+ latest_url = 'https://pypi.org/pypi/{0}/{1}/json'.format(ireq.req.name, latest)
+ latest_requires = self.session.get(latest_url)
+ for requires in latest_requires.json().get('info', {}).get('requires_dist', {}):
+ i = InstallRequirement.from_line(requires)
+ matches = [
+ r for r in releases
+ if '=={0}'.format(r) == str(ireq.req.specifier)
+ ]
+ if not matches:
+ return
+
+ if 'extra' not in repr(i.markers):
+ yield i
+ release_requires = self.session.get(
+ 'https://pypi.org/pypi/{0}/{1}/json'.format(
+ ireq.req.name, matches[0],
+ ),
+ ).json()
+ try:
+ requires_dist = release_requires['info']['requires_dist']
+ except KeyError:
+ return
+
+ for requires in requires_dist:
+ i = InstallRequirement.from_line(requires)
+ if 'extra' not in repr(i.markers):
+ yield i
+
+ try:
+ if ireq not in self._json_dep_cache:
@@ -185,12 +222,11 @@ index 1c4b943..8320e14 100644
+
+ return json_results
+
+
+ def get_legacy_dependencies(self, ireq):
"""
Given a pinned or an editable InstallRequirement, returns a set of
dependencies (also InstallRequirements, but not necessarily pinned).
@@ -139,6 +236,18 @@ class PyPIRepository(BaseRepository):
@@ -139,6 +257,24 @@ class PyPIRepository(BaseRepository):
if not (ireq.editable or is_pinned_requirement(ireq)):
raise TypeError('Expected pinned or editable InstallRequirement, got {}'.format(ireq))
@@ -203,13 +239,19 @@ index 1c4b943..8320e14 100644
+ setup_requires = self.finder.get_extras_links(
+ dist.get_metadata_lines('requires.txt')
+ )
+ except TypeError:
+ # HACK: Sometimes the InstallRequirement doesn't properly get
+ # these values set on it during the resolution process. It's
+ # difficult to pin down what is going wrong. This fixes things.
+ ireq.version = dist.version
+ ireq.project_name = dist.project_name
+ ireq.req = dist.as_requirement()
+ except (TypeError, ValueError):
+ pass
+
if ireq not in self._dependencies_cache:
if ireq.editable and (ireq.source_dir and os.path.exists(ireq.source_dir)):
# No download_dir for locally available editable requirements.
@@ -164,11 +273,14 @@ class PyPIRepository(BaseRepository):
@@ -164,11 +300,14 @@ class PyPIRepository(BaseRepository):
download_dir=download_dir,
wheel_download_dir=self._wheel_download_dir,
session=self.session,
@@ -226,7 +268,7 @@ index 1c4b943..8320e14 100644
)
except TypeError:
# Pip >= 10 (new resolver!)
@@ -190,14 +302,44 @@ class PyPIRepository(BaseRepository):
@@ -190,14 +329,44 @@ class PyPIRepository(BaseRepository):
upgrade_strategy="to-satisfy-only",
force_reinstall=False,
ignore_dependencies=False,
@@ -273,7 +315,7 @@ index 1c4b943..8320e14 100644
reqset.cleanup_files()
return set(self._dependencies_cache[ireq])
@@ -224,17 +366,10 @@ class PyPIRepository(BaseRepository):
@@ -224,17 +393,10 @@ class PyPIRepository(BaseRepository):
matching_candidates = candidates_by_version[matching_versions[0]]
return {