Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/better resolver #2267

Merged
merged 18 commits into from
May 28, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 31 additions & 24 deletions pipenv/patched/piptools/repositories/pypi.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,12 @@ def find_best_match(self, ireq, prereleases=None):
if ireq.editable:
return ireq # return itself as the best match

_all_candidates = self.find_all_candidates(ireq.name)
all_candidates = []
py_version = parse_version(os.environ.get('PIP_PYTHON_VERSION', str(sys.version_info[:3])))
for c in _all_candidates:
if c.requires_python:
python_specifier = SpecifierSet(c.requires_python)
if not python_specifier.contains(py_version):
continue
all_candidates.append(c)
all_candidates = [
c for c in self.find_all_candidates(ireq.name)
if SpecifierSet(c.requires_python).contains(py_version)
]

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),
Expand Down Expand Up @@ -200,22 +197,33 @@ def get_json_dependencies(self, ireq):
raise TypeError('Expected pinned InstallRequirement, got {}'.format(ireq))

def gen(ireq):
if self.DEFAULT_INDEX_URL in self.finder.index_urls:

url = 'https://pypi.org/pypi/{0}/json'.format(ireq.req.name)
r = self.session.get(url)

# TODO: Latest isn't always latest.
releases = list(r.json()['releases'].keys())
match = [r for r in releases if '=={0}'.format(r) == str(ireq.req.specifier)]
if match:
release_url = 'https://pypi.org/pypi/{0}/{1}/json'.format(ireq.req.name, match[0])
release_requires = self.session.get(release_url)
for requires in release_requires.json().get('info', {}).get('requires_dist', {}):
i = InstallRequirement.from_line(requires)
if self.DEFAULT_INDEX_URL not in self.finder.index_urls:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for cleaning this garbage up, seriously this was so horrible to look at

return

url = 'https://pypi.org/pypi/{0}/json'.format(ireq.req.name)
releases = self.session.get(url).json()['releases']

matches = [
r for r in releases
if '=={0}'.format(r) == str(ireq.req.specifier)
]
if not matches:
return

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

if 'extra' not in repr(i.markers):
yield i
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:
Expand All @@ -239,7 +247,6 @@ def get_dependencies(self, ireq):

return json_results


def get_legacy_dependencies(self, ireq):
"""
Given a pinned or an editable InstallRequirement, returns a set of
Expand Down
53 changes: 28 additions & 25 deletions pipenv/utils.py
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -28,9 +28,10 @@ def __init__(self, *args, **kwargs):
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:
Expand All @@ -48,7 +49,7 @@ def detach(self):
from .environments import (
PIPENV_MAX_ROUNDS,
PIPENV_CACHE_DIR,
PIPENV_MAX_RETRIES
PIPENV_MAX_RETRIES,
)

try:
Expand Down Expand Up @@ -221,8 +222,6 @@ def actually_resolve_reps(
):
from .patched.notpip._internal import basecommand
from .patched.notpip._internal.req import parse_requirements
from .patched.notpip._internal.req.req_install import InstallRequirement
from .patched.notpip._vendor import requests as pip_requests
from .patched.notpip._internal.exceptions import DistributionNotFound
from .patched.notpip._vendor.requests.exceptions import HTTPError
from pipenv.patched.piptools.resolver import Resolver
Expand All @@ -242,18 +241,18 @@ class PipCommand(basecommand.Command):
req_dir = TemporaryDirectory(suffix='-requirements', prefix='pipenv-')
cleanup_req_dir = True
for dep in deps:
if dep:
url = None
if ' -i ' in dep:
dep, url = dep.split(' -i ')
req = Requirement.from_line(dep)
_line = req.as_line()
constraints.append(_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('"', "'")
if not dep:
continue
url = None
if ' -i ' in dep:
dep, url = dep.split(' -i ')
req = Requirement.from_line(dep)
constraints.append(req.as_line())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this from_lineto_line combination do? Can’t dep be added directly into constraints?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The short answer is no, the long answer is 'a lot of things happen here'. The line we pass in could be anything, but the intermediary step ensures that, for example, if it is a local path, we tell pip to name it properly, or if it is a wheel, we do the same. This step also ensures that we format the line in the proper filesystem-appropriate format, we supply the formatted markers correctly, it essentially offers us an extra layer of sanity checking and validation. At some point we can stop passing 'deps' around at all and just pass requirement objects, I just haven't done the code cleanup for that.

# 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 = []
Expand All @@ -274,9 +273,11 @@ class PipCommand(basecommand.Command):
logging.log.verbose = True
piptools_logging.log.verbose = True
resolved_tree = set()
piptools_constraints = [c for c in parse_requirements(constraints_file, finder=pypi.finder, session=pypi.session, options=pip_options)]
resolver = Resolver(
constraints=piptools_constraints,
constraints=parse_requirements(
constraints_file,
finder=pypi.finder, session=pypi.session, options=pip_options,
),
repository=pypi,
clear_caches=clear,
prereleases=pre,
Expand Down Expand Up @@ -1119,7 +1120,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):
Expand All @@ -1128,7 +1129,9 @@ 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):
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 = []
Expand Down