Files
Dan Ryan 7429881693 Add a proper parser for index urls internally
- Fix invocation of vcs hash retrieval
- Add error handling for pip url retrieval

Signed-off-by: Dan Ryan <dan@danryan.co>
2018-10-07 05:25:58 -04:00

512 lines
21 KiB
Diff

diff --git a/pipenv/patched/pip/_internal/download.py b/pipenv/patched/pip/_internal/download.py
index 96f3b65c..3fb4ebef 100644
--- a/pipenv/patched/pip/_internal/download.py
+++ b/pipenv/patched/pip/_internal/download.py
@@ -19,6 +19,7 @@ from pip._vendor.lockfile import LockError
from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter
from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth
from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response
+from pip._vendor.requests.sessions import Session
from pip._vendor.requests.structures import CaseInsensitiveDict
from pip._vendor.requests.utils import get_netrc_auth
# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is
@@ -69,7 +70,7 @@ def user_agent():
Return a string representing the user agent.
"""
data = {
- "installer": {"name": "pip", "version": pip.__version__},
+ "installer": {"name": "pip", "version": pipenv.patched.notpip.__version__},
"python": platform.python_version(),
"implementation": {
"name": platform.python_implementation(),
@@ -322,7 +323,7 @@ class InsecureHTTPAdapter(HTTPAdapter):
conn.ca_certs = None
-class PipSession(requests.Session):
+class PipSession(Session):
timeout = None
@@ -752,7 +753,7 @@ def _copy_dist_from_dir(link_path, location):
# build an sdist
setup_py = 'setup.py'
- sdist_args = [sys.executable]
+ sdist_args = [os.environ.get('PIP_PYTHON_PATH', sys.executable)]
sdist_args.append('-c')
sdist_args.append(SETUPTOOLS_SHIM % setup_py)
sdist_args.append('sdist')
diff --git a/pipenv/patched/pip/_internal/index.py b/pipenv/patched/pip/_internal/index.py
index 8c0ec82c..ad00ba04 100644
--- a/pipenv/patched/pip/_internal/index.py
+++ b/pipenv/patched/pip/_internal/index.py
@@ -58,11 +58,12 @@ logger = logging.getLogger(__name__)
class InstallationCandidate(object):
- def __init__(self, project, version, location):
+ def __init__(self, project, version, location, requires_python=None):
self.project = project
self.version = parse_version(version)
self.location = location
self._key = (self.project, self.version, self.location)
+ self.requires_python = requires_python
def __repr__(self):
return "<InstallationCandidate({!r}, {!r}, {!r})>".format(
@@ -168,6 +169,9 @@ class PackageFinder(object):
# The Session we'll use to make requests
self.session = session
+ # Kenneth's Hack
+ self.extra = None
+
# The valid tags to check potential found wheel candidates against
self.valid_tags = get_supported(
versions=versions,
@@ -220,6 +224,24 @@ class PackageFinder(object):
)
self.dependency_links.extend(links)
+ @staticmethod
+ def get_extras_links(links):
+ requires = []
+ extras = {}
+
+ current_list = requires
+
+ for link in links:
+ if not link:
+ current_list = requires
+ if link.startswith('['):
+ current_list = []
+ extras[link[1:-1]] = current_list
+ else:
+ current_list.append(link)
+
+ return extras
+
@staticmethod
def _sort_locations(locations, expand_dir=False):
"""
@@ -272,7 +294,7 @@ class PackageFinder(object):
return files, urls
- def _candidate_sort_key(self, candidate):
+ def _candidate_sort_key(self, candidate, ignore_compatibility=True):
"""
Function used to generate link sort key for link tuples.
The greater the return value, the more preferred it is.
@@ -292,14 +314,19 @@ class PackageFinder(object):
if candidate.location.is_wheel:
# can raise InvalidWheelFilename
wheel = Wheel(candidate.location.filename)
- if not wheel.supported(self.valid_tags):
+ if not wheel.supported(self.valid_tags) and not ignore_compatibility:
raise UnsupportedWheel(
"%s is not a supported wheel for this platform. It "
"can't be sorted." % wheel.filename
)
if self.prefer_binary:
binary_preference = 1
- pri = -(wheel.support_index_min(self.valid_tags))
+ tags = self.valid_tags if not ignore_compatibility else None
+ try:
+ pri = -(wheel.support_index_min(tags=tags))
+ except TypeError:
+ pri = -(support_num)
+
if wheel.build_tag is not None:
match = re.match(r'^(\d+)(.*)$', wheel.build_tag)
build_tag_groups = match.groups()
@@ -484,7 +511,7 @@ class PackageFinder(object):
dependency_versions
)
- def find_requirement(self, req, upgrade):
+ def find_requirement(self, req, upgrade, ignore_compatibility=False):
"""Try to find a Link matching req
Expects req, an InstallRequirement and upgrade, a boolean
@@ -594,8 +621,9 @@ class PackageFinder(object):
continue
seen.add(location)
- page = self._get_page(location)
- if page is None:
+ try:
+ page = self._get_page(location)
+ except requests.HTTPError as e:
continue
yield page
@@ -631,7 +659,7 @@ class PackageFinder(object):
logger.debug('Skipping link %s; %s', link, reason)
self.logged_links.add(link)
- def _link_package_versions(self, link, search):
+ def _link_package_versions(self, link, search, ignore_compatibility=True):
"""Return an InstallationCandidate or None"""
version = None
if link.egg_fragment:
@@ -647,12 +675,12 @@ class PackageFinder(object):
link, 'unsupported archive format: %s' % ext,
)
return
- if "binary" not in search.formats and ext == wheel_ext:
+ if "binary" not in search.formats and ext == wheel_ext and not ignore_compatibility:
self._log_skipped_link(
link, 'No binaries permitted for %s' % search.supplied,
)
return
- if "macosx10" in link.path and ext == '.zip':
+ if "macosx10" in link.path and ext == '.zip' and not ignore_compatibility:
self._log_skipped_link(link, 'macosx10 one')
return
if ext == wheel_ext:
@@ -666,7 +694,7 @@ class PackageFinder(object):
link, 'wrong project name (not %s)' % search.supplied)
return
- if not wheel.supported(self.valid_tags):
+ if not wheel.supported(self.valid_tags) and not ignore_compatibility:
self._log_skipped_link(
link, 'it is not compatible with this Python')
return
@@ -702,14 +730,14 @@ class PackageFinder(object):
link.filename, link.requires_python)
support_this_python = True
- if not support_this_python:
+ if not support_this_python and not ignore_compatibility:
logger.debug("The package %s is incompatible with the python"
"version in use. Acceptable python versions are:%s",
link, link.requires_python)
return
logger.debug('Found link %s, version: %s', link, version)
- return InstallationCandidate(search.supplied, version, link)
+ return InstallationCandidate(search.supplied, version, link, link.requires_python)
def _get_page(self, link):
return HTMLPage.get_page(link, session=self.session)
diff --git a/pipenv/patched/pip/_internal/operations/prepare.py b/pipenv/patched/pip/_internal/operations/prepare.py
index 7740c284..b6e946d8 100644
--- a/pipenv/patched/pip/_internal/operations/prepare.py
+++ b/pipenv/patched/pip/_internal/operations/prepare.py
@@ -17,7 +17,7 @@ from pip._internal.exceptions import (
)
from pip._internal.utils.hashes import MissingHashes
from pip._internal.utils.logging import indent_log
-from pip._internal.utils.misc import display_path, normalize_path
+from pip._internal.utils.misc import display_path, normalize_path, rmtree
from pip._internal.vcs import vcs
logger = logging.getLogger(__name__)
@@ -123,7 +123,11 @@ class IsSDist(DistAbstraction):
"Installing build dependencies"
)
- self.req.run_egg_info()
+ try:
+ self.req.run_egg_info()
+ except (OSError, TypeError):
+ self.req._correct_build_location()
+ self.req.run_egg_info()
self.req.assert_source_matches_version()
@@ -205,16 +209,8 @@ class RequirementPreparer(object):
# installation.
# FIXME: this won't upgrade when there's an existing
# package unpacked in `req.source_dir`
- # package unpacked in `req.source_dir`
if os.path.exists(os.path.join(req.source_dir, 'setup.py')):
- raise PreviousBuildDirError(
- "pip can't proceed with requirements '%s' due to a"
- " pre-existing build directory (%s). This is "
- "likely due to a previous installation that failed"
- ". pip is being responsible and not assuming it "
- "can delete this. Please delete it and try again."
- % (req, req.source_dir)
- )
+ rmtree(req.source_dir)
req.populate_link(finder, upgrade_allowed, require_hashes)
# We can't hit this spot and have populate_link return None.
diff --git a/pipenv/patched/pip/_internal/pep425tags.py b/pipenv/patched/pip/_internal/pep425tags.py
index 0b5c7832..bea31585 100644
--- a/pipenv/patched/pip/_internal/pep425tags.py
+++ b/pipenv/patched/pip/_internal/pep425tags.py
@@ -10,7 +10,10 @@ import sysconfig
import warnings
from collections import OrderedDict
-import pip._internal.utils.glibc
+try:
+ import pip._internal.utils.glibc
+except ImportError:
+ import pip.utils.glibc
logger = logging.getLogger(__name__)
diff --git a/pipenv/patched/pip/_internal/req/req_install.py b/pipenv/patched/pip/_internal/req/req_install.py
index 462c80aa..d039adc8 100644
--- a/pipenv/patched/pip/_internal/req/req_install.py
+++ b/pipenv/patched/pip/_internal/req/req_install.py
@@ -615,7 +615,7 @@ class InstallRequirement(object):
with indent_log():
script = SETUPTOOLS_SHIM % self.setup_py
- base_cmd = [sys.executable, '-c', script]
+ base_cmd = [os.environ.get('PIP_PYTHON_PATH', sys.executable), '-c', script]
if self.isolated:
base_cmd += ["--no-user-cfg"]
egg_info_cmd = base_cmd + ['egg_info']
@@ -797,7 +797,7 @@ class InstallRequirement(object):
with self.build_env:
call_subprocess(
[
- sys.executable,
+ os.environ.get('PIP_PYTHON_PATH', sys.executable),
'-c',
SETUPTOOLS_SHIM % self.setup_py
] +
@@ -1015,7 +1015,7 @@ class InstallRequirement(object):
def get_install_args(self, global_options, record_filename, root, prefix,
pycompile):
- install_args = [sys.executable, "-u"]
+ install_args = [os.environ.get('PIP_PYTHON_PATH', sys.executable), "-u"]
install_args.append('-c')
install_args.append(SETUPTOOLS_SHIM % self.setup_py)
install_args += list(global_options) + \
diff --git a/pipenv/patched/pip/_internal/req/req_set.py b/pipenv/patched/pip/_internal/req/req_set.py
index 2bc6b745..e552afc1 100644
--- a/pipenv/patched/pip/_internal/req/req_set.py
+++ b/pipenv/patched/pip/_internal/req/req_set.py
@@ -12,7 +12,7 @@ logger = logging.getLogger(__name__)
class RequirementSet(object):
- def __init__(self, require_hashes=False):
+ def __init__(self, require_hashes=False, ignore_compatibility=True):
"""Create a RequirementSet.
"""
@@ -24,6 +24,7 @@ class RequirementSet(object):
self.unnamed_requirements = []
self.successfully_downloaded = []
self.reqs_to_cleanup = []
+ self.ignore_compatibility = ignore_compatibility
def __str__(self):
reqs = [req for req in self.requirements.values()
@@ -65,7 +66,7 @@ class RequirementSet(object):
# environment markers.
if install_req.link and install_req.link.is_wheel:
wheel = Wheel(install_req.link.filename)
- if not wheel.supported():
+ if not wheel.supported() and not self.ignore_compatibility:
raise InstallationError(
"%s is not a supported wheel on this platform." %
wheel.filename
@@ -151,7 +152,7 @@ class RequirementSet(object):
return self.requirements[name]
if name in self.requirement_aliases:
return self.requirements[self.requirement_aliases[name]]
- raise KeyError("No project with the name %r" % project_name)
+ # raise KeyError("No project with the name %r" % project_name)
def cleanup_files(self):
"""Clean up files, remove builds."""
diff --git a/pipenv/patched/pip/_internal/resolve.py b/pipenv/patched/pip/_internal/resolve.py
index 8480e48c..ffc4aa7d 100644
--- a/pipenv/patched/pip/_internal/resolve.py
+++ b/pipenv/patched/pip/_internal/resolve.py
@@ -35,7 +35,7 @@ class Resolver(object):
def __init__(self, preparer, session, finder, wheel_cache, use_user_site,
ignore_dependencies, ignore_installed, ignore_requires_python,
- force_reinstall, isolated, upgrade_strategy):
+ force_reinstall, isolated, upgrade_strategy, ignore_compatibility=False):
super(Resolver, self).__init__()
assert upgrade_strategy in self._allowed_strategies
@@ -55,7 +55,11 @@ class Resolver(object):
self.ignore_dependencies = ignore_dependencies
self.ignore_installed = ignore_installed
self.ignore_requires_python = ignore_requires_python
+ self.ignore_compatibility = ignore_compatibility
self.use_user_site = use_user_site
+ self.requires_python = None
+ if self.ignore_compatibility:
+ self.ignore_requires_python = True
self._discovered_dependencies = defaultdict(list)
@@ -237,7 +241,7 @@ class Resolver(object):
return abstract_dist
- def _resolve_one(self, requirement_set, req_to_install):
+ def _resolve_one(self, requirement_set, req_to_install, ignore_requires_python=False):
"""Prepare a single requirements file.
:return: A list of additional InstallRequirements to also install.
@@ -245,6 +249,9 @@ class Resolver(object):
# Tell user what we are doing for this requirement:
# obtain (editable), skipping, processing (local url), collecting
# (remote url or package name)
+ if ignore_requires_python or self.ignore_requires_python:
+ self.ignore_compatibility = True
+
if req_to_install.constraint or req_to_install.prepared:
return []
@@ -260,11 +267,17 @@ class Resolver(object):
try:
check_dist_requires_python(dist)
except UnsupportedPythonVersion as err:
- if self.ignore_requires_python:
+ if self.ignore_compatibility:
logger.warning(err.args[0])
else:
raise
+ # A huge hack, by Kenneth Reitz.
+ try:
+ self.requires_python = check_dist_requires_python(dist, absorb=False)
+ except TypeError:
+ self.requires_python = None
+
more_reqs = []
def add_req(subreq, extras_requested):
@@ -290,10 +303,14 @@ class Resolver(object):
# We add req_to_install before its dependencies, so that we
# can refer to it when adding dependencies.
if not requirement_set.has_requirement(req_to_install.name):
+ available_requested = sorted(
+ set(dist.extras) & set(req_to_install.extras)
+ )
# 'unnamed' requirements will get added here
req_to_install.is_direct = True
requirement_set.add_requirement(
req_to_install, parent_req_name=None,
+ extras_requested=available_requested,
)
if not self.ignore_dependencies:
@@ -317,6 +334,19 @@ class Resolver(object):
for subreq in dist.requires(available_requested):
add_req(subreq, extras_requested=available_requested)
+ # Hack for deep-resolving extras.
+ for available in available_requested:
+ if hasattr(dist, '_DistInfoDistribution__dep_map'):
+ for req in dist._DistInfoDistribution__dep_map[available]:
+ req = InstallRequirement.from_req(
+ str(req),
+ req_to_install,
+ isolated=self.isolated,
+ wheel_cache=self.wheel_cache,
+ )
+
+ more_reqs.append(req)
+
if not req_to_install.editable and not req_to_install.satisfied_by:
# XXX: --no-install leads this to report 'Successfully
# downloaded' for only non-editable reqs, even though we took
diff --git a/pipenv/patched/pip/_internal/utils/misc.py b/pipenv/patched/pip/_internal/utils/misc.py
index 3236af63..439a831d 100644
--- a/pipenv/patched/pip/_internal/utils/misc.py
+++ b/pipenv/patched/pip/_internal/utils/misc.py
@@ -96,7 +96,7 @@ def get_prog():
try:
prog = os.path.basename(sys.argv[0])
if prog in ('__main__.py', '-c'):
- return "%s -m pip" % sys.executable
+ return "%s -m pip" % os.environ.get('PIP_PYTHON_PATH', sys.executable)
else:
return prog
except (AttributeError, TypeError, IndexError):
diff --git a/pipenv/patched/pip/_internal/utils/packaging.py b/pipenv/patched/pip/_internal/utils/packaging.py
index 5f9bb93d..276a9ccc 100644
--- a/pipenv/patched/pip/_internal/utils/packaging.py
+++ b/pipenv/patched/pip/_internal/utils/packaging.py
@@ -28,7 +28,7 @@ def check_requires_python(requires_python):
requires_python_specifier = specifiers.SpecifierSet(requires_python)
# We only use major.minor.micro
- python_version = version.parse('.'.join(map(str, sys.version_info[:3])))
+ python_version = version.parse('{0}.{1}.{2}'.format(*sys.version_info[:3]))
return python_version in requires_python_specifier
@@ -40,20 +40,17 @@ def get_metadata(dist):
return dist.get_metadata('PKG-INFO')
-def check_dist_requires_python(dist):
+def check_dist_requires_python(dist, absorb=True):
metadata = get_metadata(dist)
feed_parser = FeedParser()
feed_parser.feed(metadata)
pkg_info_dict = feed_parser.close()
requires_python = pkg_info_dict.get('Requires-Python')
+ if not absorb:
+ return requires_python
try:
if not check_requires_python(requires_python):
- raise exceptions.UnsupportedPythonVersion(
- "%s requires Python '%s' but the running Python is %s" % (
- dist.project_name,
- requires_python,
- '.'.join(map(str, sys.version_info[:3])),)
- )
+ return requires_python
except specifiers.InvalidSpecifier as e:
logger.warning(
"Package %s has an invalid Requires-Python entry %s - %s",
diff --git a/pipenv/patched/pip/_internal/wheel.py b/pipenv/patched/pip/_internal/wheel.py
index fcf9d3d3..d8aff848 100644
--- a/pipenv/patched/pip/_internal/wheel.py
+++ b/pipenv/patched/pip/_internal/wheel.py
@@ -83,7 +83,7 @@ def fix_script(path):
firstline = script.readline()
if not firstline.startswith(b'#!python'):
return False
- exename = sys.executable.encode(sys.getfilesystemencoding())
+ exename = os.environ.get('PIP_PYTHON_PATH', sys.executable).encode(sys.getfilesystemencoding())
firstline = b'#!' + exename + os.linesep.encode("ascii")
rest = script.read()
with open(path, 'wb') as script:
@@ -665,7 +665,7 @@ class WheelBuilder(object):
# relies on site.py to find parts of the standard library outside the
# virtualenv.
return [
- sys.executable, '-u', '-c',
+ os.environ.get('PIP_PYTHON_PATH', sys.executable), '-u', '-c',
SETUPTOOLS_SHIM % req.setup_py
] + list(self.global_options)
diff --git a/pipenv/patched/pip/_internal/index.py b/pipenv/patched/pip/_internal/index.py
index 793dd1cb..426880e9 100644
--- a/pipenv/patched/pip/_internal/index.py
+++ b/pipenv/patched/pip/_internal/index.py
@@ -477,7 +477,10 @@ class PackageFinder(object):
page_versions = []
for page in self._get_pages(url_locations, project_name):
- logger.debug('Analyzing links from page %s', page.url)
+ try:
+ logger.debug('Analyzing links from page %s', page.url)
+ except AttributeError:
+ continue
with indent_log():
page_versions.extend(
self._package_versions(page.links, search)