mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
Finish updating vendored deps
Signed-off-by: Dan Ryan <dan@danryan.co> Update delegator patch Signed-off-by: Dan Ryan <dan@danryan.co> Update patches for pip Signed-off-by: Dan Ryan <dan@danryan.co> Update vendored deps Signed-off-by: Dan Ryan <dan@danryan.co> Update patches Signed-off-by: Dan Ryan <dan@danryan.co> Fix imports to use pip shims instead of direct imports Signed-off-by: Dan Ryan <dan@danryan.co> Update vendoring scripts and pip shims Signed-off-by: Dan Ryan <dan@danryan.co> Log to stdout in real time during verbose logging Signed-off-by: Dan Ryan <dan@danryan.co> Don’t log environment Fix unicode decoding issues Signed-off-by: Dan Ryan <dan@danryan.co> Only set buffers on ttys Signed-off-by: Dan Ryan <dan@danryan.co> Fix typo Signed-off-by: Dan Ryan <dan@danryan.co> Use default encodings Signed-off-by: Dan Ryan <dan@danryan.co> Fix encodings and run only failing tests Signed-off-by: Dan Ryan <dan@danryan.co>
This commit is contained in:
@@ -4,6 +4,7 @@ steps:
|
||||
pip install certifi
|
||||
python -m certifi > cacert.txt
|
||||
Write-Host "##vso[task.setvariable variable=GIT_SSL_CAINFO]$(Get-Content cacert.txt)"
|
||||
$env:GIT_SSL_CAINFO="$(Get-Content cacert.txt)"
|
||||
# Shorten paths to get under MAX_PATH or else integration tests will fail
|
||||
# https://bugs.python.org/issue18199
|
||||
subst T: "$env:TEMP"
|
||||
@@ -11,8 +12,7 @@ steps:
|
||||
$env:TEMP='T:\'
|
||||
Write-Host "##vso[task.setvariable variable=TMP]T:\"
|
||||
$env:TEMP='T:\'
|
||||
Get-ChildItem Env:
|
||||
D:\.venv\Scripts\pipenv run pytest -n 4 -ra --ignore=pipenv\patched --ignore=pipenv\vendor --junitxml=test-results.xml tests
|
||||
D:\.venv\Scripts\pipenv run pytest -ra --ignore=pipenv\patched --ignore=pipenv\vendor -k 'test_get_vcs_refs or test_install_editable_git_tag' --junitxml=test-results.xml tests
|
||||
displayName: Run integration tests
|
||||
|
||||
- task: PublishTestResults@2
|
||||
|
||||
+19
-1
@@ -1,9 +1,13 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
# |~~\' |~~
|
||||
# |__/||~~\|--|/~\\ /
|
||||
# | ||__/|__| |\/
|
||||
# |
|
||||
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from .__version__ import __version__
|
||||
|
||||
PIPENV_ROOT = os.path.dirname(os.path.realpath(__file__))
|
||||
@@ -13,14 +17,28 @@ PIPENV_PATCHED = os.sep.join([PIPENV_ROOT, "patched"])
|
||||
sys.path.insert(0, PIPENV_VENDOR)
|
||||
# Inject patched directory into system path.
|
||||
sys.path.insert(0, PIPENV_PATCHED)
|
||||
from vistir.compat import fs_str
|
||||
|
||||
from pipenv.vendor.urllib3.exceptions import DependencyWarning
|
||||
from pipenv.vendor.vistir.compat import ResourceWarning, fs_str
|
||||
warnings.filterwarnings("ignore", category=DependencyWarning)
|
||||
warnings.filterwarnings("ignore", category=ResourceWarning)
|
||||
|
||||
if sys.version_info >= (3, 1) and sys.version_info <= (3, 6):
|
||||
if sys.stdout.isatty() and sys.stderr.isatty():
|
||||
import io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf8')
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf8')
|
||||
|
||||
os.environ["PIP_DISABLE_PIP_VERSION_CHECK"] = fs_str("1")
|
||||
os.environ["PIP_SHIMS_BASE_MODULE"] = fs_str("pipenv.patched.notpip")
|
||||
|
||||
# Hack to make things work better.
|
||||
try:
|
||||
if "concurrency" in sys.modules:
|
||||
del sys.modules["concurrency"]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
from .cli import cli
|
||||
from . import resolver
|
||||
|
||||
|
||||
@@ -300,3 +300,79 @@ def NamedTemporaryFile(
|
||||
os.unlink(name)
|
||||
os.close(fd)
|
||||
raise
|
||||
|
||||
|
||||
def getpreferredencoding():
|
||||
import locale
|
||||
# Borrowed from Invoke
|
||||
# (see https://github.com/pyinvoke/invoke/blob/93af29d/invoke/runners.py#L881)
|
||||
_encoding = locale.getpreferredencoding(False)
|
||||
if six.PY2 and not sys.platform == "win32":
|
||||
_default_encoding = locale.getdefaultlocale()[1]
|
||||
if _default_encoding is not None:
|
||||
_encoding = _default_encoding
|
||||
return _encoding
|
||||
|
||||
|
||||
DEFAULT_ENCODING = getpreferredencoding()
|
||||
|
||||
|
||||
# From https://github.com/CarlFK/veyepar/blob/5c5de47/dj/scripts/fixunicode.py
|
||||
# MIT LIcensed, thanks Carl!
|
||||
def force_encoding():
|
||||
try:
|
||||
stdout_isatty = sys.stdout.isatty
|
||||
stderr_isatty = sys.stderr.isatty
|
||||
except AttributeError:
|
||||
return DEFAULT_ENCODING, DEFAULT_ENCODING
|
||||
else:
|
||||
if not (stdout_isatty() and stderr_isatty()):
|
||||
return DEFAULT_ENCODING, DEFAULT_ENCODING
|
||||
stdout_encoding = sys.stdout.encoding
|
||||
stderr_encoding = sys.stderr.encoding
|
||||
if sys.platform == "win32" and sys.version_info >= (3, 1):
|
||||
return DEFAULT_ENCODING, DEFAULT_ENCODING
|
||||
if stdout_encoding.lower() != "utf-8" or stderr_encoding.lower() != "utf-8":
|
||||
|
||||
from ctypes import pythonapi, py_object, c_char_p
|
||||
try:
|
||||
PyFile_SetEncoding = pythonapi.PyFile_SetEncoding
|
||||
except AttributeError:
|
||||
return DEFAULT_ENCODING, DEFAULT_ENCODING
|
||||
else:
|
||||
PyFile_SetEncoding.argtypes = (py_object, c_char_p)
|
||||
if stdout_encoding.lower() != "utf-8":
|
||||
try:
|
||||
was_set = PyFile_SetEncoding(sys.stdout, "utf-8")
|
||||
except OSError:
|
||||
was_set = False
|
||||
if not was_set:
|
||||
stdout_encoding = DEFAULT_ENCODING
|
||||
else:
|
||||
stdout_encoding = "utf-8"
|
||||
|
||||
if stderr_encoding.lower() != "utf-8":
|
||||
try:
|
||||
was_set = PyFile_SetEncoding(sys.stderr, "utf-8")
|
||||
except OSError:
|
||||
was_set = False
|
||||
if not was_set:
|
||||
stderr_encoding = DEFAULT_ENCODING
|
||||
else:
|
||||
stderr_encoding = "utf-8"
|
||||
|
||||
return stdout_encoding, stderr_encoding
|
||||
|
||||
|
||||
OUT_ENCODING, ERR_ENCODING = force_encoding()
|
||||
|
||||
|
||||
def decode_output(output):
|
||||
if not isinstance(output, six.string_types):
|
||||
return output
|
||||
try:
|
||||
output = output.encode(DEFAULT_ENCODING)
|
||||
except AttributeError:
|
||||
pass
|
||||
output = output.decode(DEFAULT_ENCODING)
|
||||
return output
|
||||
|
||||
+10
-9
@@ -1,4 +1,5 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
|
||||
import contextlib
|
||||
import logging
|
||||
import os
|
||||
@@ -105,9 +106,13 @@ UNICODE_TO_ASCII_TRANSLATION_MAP = {
|
||||
def fix_utf8(text):
|
||||
if not isinstance(text, six.string_types):
|
||||
return text
|
||||
if six.PY2:
|
||||
text = unicode.translate(vistir.misc.to_text(text), UNICODE_TO_ASCII_TRANSLATION_MAP)
|
||||
return u"{0}".format(text)
|
||||
from ._compat import decode_output
|
||||
try:
|
||||
text = decode_output(text)
|
||||
except UnicodeDecodeError:
|
||||
if six.PY2:
|
||||
text = unicode.translate(vistir.misc.to_text(text), UNICODE_TO_ASCII_TRANSLATION_MAP)
|
||||
return text
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
@@ -230,7 +235,7 @@ def cleanup_virtualenv(bare=True):
|
||||
|
||||
def import_requirements(r=None, dev=False):
|
||||
from .patched.notpip._vendor import requests as pip_requests
|
||||
from .patched.notpip._internal.req.req_file import parse_requirements
|
||||
from .vendor.pip_shims.shims import parse_requirements
|
||||
|
||||
# Parse requirements.txt file with Pip's parser.
|
||||
# Pip requires a `PipSession` which is a subclass of requests.Session.
|
||||
@@ -1754,7 +1759,7 @@ def do_install(
|
||||
selective_upgrade=False,
|
||||
):
|
||||
from .environments import PIPENV_VIRTUALENV, PIPENV_USE_SYSTEM
|
||||
from notpip._internal.exceptions import PipError
|
||||
from .vendor.pip_shims.shims import PipError
|
||||
|
||||
requirements_directory = vistir.path.create_tracked_tempdir(
|
||||
suffix="-requirements", prefix="pipenv-"
|
||||
@@ -2212,10 +2217,6 @@ def _launch_windows_subprocess(script):
|
||||
|
||||
command = system_which(script.command)
|
||||
options = {"universal_newlines": True}
|
||||
env_strings = [
|
||||
vistir.compat.to_native_string("{0}: {1}".format(k, v)) for k, v in os.environ.items()
|
||||
]
|
||||
click.echo(vistir.compat.to_native_string("\n".join(env_strings)), err=True)
|
||||
|
||||
# Command not found, maybe this is a shell built-in?
|
||||
if not command:
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "18.0"
|
||||
__version__ = "18.1"
|
||||
|
||||
@@ -4,7 +4,6 @@ from __future__ import absolute_import
|
||||
import locale
|
||||
import logging
|
||||
import os
|
||||
import optparse
|
||||
import warnings
|
||||
|
||||
import sys
|
||||
@@ -38,17 +37,12 @@ else:
|
||||
else:
|
||||
securetransport.inject_into_urllib3()
|
||||
|
||||
from pipenv.patched.notpip import __version__
|
||||
from pipenv.patched.notpip._internal import cmdoptions
|
||||
from pipenv.patched.notpip._internal.exceptions import CommandError, PipError
|
||||
from pipenv.patched.notpip._internal.utils.misc import get_installed_distributions, get_prog
|
||||
from pipenv.patched.notpip._internal.cli.autocompletion import autocomplete
|
||||
from pipenv.patched.notpip._internal.cli.main_parser import parse_command
|
||||
from pipenv.patched.notpip._internal.commands import commands_dict
|
||||
from pipenv.patched.notpip._internal.exceptions import PipError
|
||||
from pipenv.patched.notpip._internal.utils import deprecation
|
||||
from pipenv.patched.notpip._internal.vcs import git, mercurial, subversion, bazaar # noqa
|
||||
from pipenv.patched.notpip._internal.baseparser import (
|
||||
ConfigOptionParser, UpdatingDefaultsHelpFormatter,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.commands import get_summaries, get_similar_commands
|
||||
from pipenv.patched.notpip._internal.commands import commands_dict
|
||||
from pipenv.patched.notpip._vendor.urllib3.exceptions import InsecureRequestWarning
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -57,232 +51,6 @@ logger = logging.getLogger(__name__)
|
||||
warnings.filterwarnings("ignore", category=InsecureRequestWarning)
|
||||
|
||||
|
||||
def autocomplete():
|
||||
"""Command and option completion for the main option parser (and options)
|
||||
and its subcommands (and options).
|
||||
|
||||
Enable by sourcing one of the completion shell scripts (bash, zsh or fish).
|
||||
"""
|
||||
# Don't complete if user hasn't sourced bash_completion file.
|
||||
if 'PIP_AUTO_COMPLETE' not in os.environ:
|
||||
return
|
||||
cwords = os.environ['COMP_WORDS'].split()[1:]
|
||||
cword = int(os.environ['COMP_CWORD'])
|
||||
try:
|
||||
current = cwords[cword - 1]
|
||||
except IndexError:
|
||||
current = ''
|
||||
|
||||
subcommands = [cmd for cmd, summary in get_summaries()]
|
||||
options = []
|
||||
# subcommand
|
||||
try:
|
||||
subcommand_name = [w for w in cwords if w in subcommands][0]
|
||||
except IndexError:
|
||||
subcommand_name = None
|
||||
|
||||
parser = create_main_parser()
|
||||
# subcommand options
|
||||
if subcommand_name:
|
||||
# special case: 'help' subcommand has no options
|
||||
if subcommand_name == 'help':
|
||||
sys.exit(1)
|
||||
# special case: list locally installed dists for show and uninstall
|
||||
should_list_installed = (
|
||||
subcommand_name in ['show', 'uninstall'] and
|
||||
not current.startswith('-')
|
||||
)
|
||||
if should_list_installed:
|
||||
installed = []
|
||||
lc = current.lower()
|
||||
for dist in get_installed_distributions(local_only=True):
|
||||
if dist.key.startswith(lc) and dist.key not in cwords[1:]:
|
||||
installed.append(dist.key)
|
||||
# if there are no dists installed, fall back to option completion
|
||||
if installed:
|
||||
for dist in installed:
|
||||
print(dist)
|
||||
sys.exit(1)
|
||||
|
||||
subcommand = commands_dict[subcommand_name]()
|
||||
|
||||
for opt in subcommand.parser.option_list_all:
|
||||
if opt.help != optparse.SUPPRESS_HELP:
|
||||
for opt_str in opt._long_opts + opt._short_opts:
|
||||
options.append((opt_str, opt.nargs))
|
||||
|
||||
# filter out previously specified options from available options
|
||||
prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]]
|
||||
options = [(x, v) for (x, v) in options if x not in prev_opts]
|
||||
# filter options by current input
|
||||
options = [(k, v) for k, v in options if k.startswith(current)]
|
||||
# get completion type given cwords and available subcommand options
|
||||
completion_type = get_path_completion_type(
|
||||
cwords, cword, subcommand.parser.option_list_all,
|
||||
)
|
||||
# get completion files and directories if ``completion_type`` is
|
||||
# ``<file>``, ``<dir>`` or ``<path>``
|
||||
if completion_type:
|
||||
options = auto_complete_paths(current, completion_type)
|
||||
options = ((opt, 0) for opt in options)
|
||||
for option in options:
|
||||
opt_label = option[0]
|
||||
# append '=' to options which require args
|
||||
if option[1] and option[0][:2] == "--":
|
||||
opt_label += '='
|
||||
print(opt_label)
|
||||
else:
|
||||
# show main parser options only when necessary
|
||||
|
||||
opts = [i.option_list for i in parser.option_groups]
|
||||
opts.append(parser.option_list)
|
||||
opts = (o for it in opts for o in it)
|
||||
if current.startswith('-'):
|
||||
for opt in opts:
|
||||
if opt.help != optparse.SUPPRESS_HELP:
|
||||
subcommands += opt._long_opts + opt._short_opts
|
||||
else:
|
||||
# get completion type given cwords and all available options
|
||||
completion_type = get_path_completion_type(cwords, cword, opts)
|
||||
if completion_type:
|
||||
subcommands = auto_complete_paths(current, completion_type)
|
||||
|
||||
print(' '.join([x for x in subcommands if x.startswith(current)]))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_path_completion_type(cwords, cword, opts):
|
||||
"""Get the type of path completion (``file``, ``dir``, ``path`` or None)
|
||||
|
||||
:param cwords: same as the environmental variable ``COMP_WORDS``
|
||||
:param cword: same as the environmental variable ``COMP_CWORD``
|
||||
:param opts: The available options to check
|
||||
:return: path completion type (``file``, ``dir``, ``path`` or None)
|
||||
"""
|
||||
if cword < 2 or not cwords[cword - 2].startswith('-'):
|
||||
return
|
||||
for opt in opts:
|
||||
if opt.help == optparse.SUPPRESS_HELP:
|
||||
continue
|
||||
for o in str(opt).split('/'):
|
||||
if cwords[cword - 2].split('=')[0] == o:
|
||||
if any(x in ('path', 'file', 'dir')
|
||||
for x in opt.metavar.split('/')):
|
||||
return opt.metavar
|
||||
|
||||
|
||||
def auto_complete_paths(current, completion_type):
|
||||
"""If ``completion_type`` is ``file`` or ``path``, list all regular files
|
||||
and directories starting with ``current``; otherwise only list directories
|
||||
starting with ``current``.
|
||||
|
||||
:param current: The word to be completed
|
||||
:param completion_type: path completion type(`file`, `path` or `dir`)i
|
||||
:return: A generator of regular files and/or directories
|
||||
"""
|
||||
directory, filename = os.path.split(current)
|
||||
current_path = os.path.abspath(directory)
|
||||
# Don't complete paths if they can't be accessed
|
||||
if not os.access(current_path, os.R_OK):
|
||||
return
|
||||
filename = os.path.normcase(filename)
|
||||
# list all files that start with ``filename``
|
||||
file_list = (x for x in os.listdir(current_path)
|
||||
if os.path.normcase(x).startswith(filename))
|
||||
for f in file_list:
|
||||
opt = os.path.join(current_path, f)
|
||||
comp_file = os.path.normcase(os.path.join(directory, f))
|
||||
# complete regular files when there is not ``<dir>`` after option
|
||||
# complete directories when there is ``<file>``, ``<path>`` or
|
||||
# ``<dir>``after option
|
||||
if completion_type != 'dir' and os.path.isfile(opt):
|
||||
yield comp_file
|
||||
elif os.path.isdir(opt):
|
||||
yield os.path.join(comp_file, '')
|
||||
|
||||
|
||||
def create_main_parser():
|
||||
parser_kw = {
|
||||
'usage': '\n%prog <command> [options]',
|
||||
'add_help_option': False,
|
||||
'formatter': UpdatingDefaultsHelpFormatter(),
|
||||
'name': 'global',
|
||||
'prog': get_prog(),
|
||||
}
|
||||
|
||||
parser = ConfigOptionParser(**parser_kw)
|
||||
parser.disable_interspersed_args()
|
||||
|
||||
pip_pkg_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
parser.version = 'pip %s from %s (python %s)' % (
|
||||
__version__, pip_pkg_dir, sys.version[:3],
|
||||
)
|
||||
|
||||
# add the general options
|
||||
gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser)
|
||||
parser.add_option_group(gen_opts)
|
||||
|
||||
parser.main = True # so the help formatter knows
|
||||
|
||||
# create command listing for description
|
||||
command_summaries = get_summaries()
|
||||
description = [''] + ['%-27s %s' % (i, j) for i, j in command_summaries]
|
||||
parser.description = '\n'.join(description)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def parseopts(args):
|
||||
parser = create_main_parser()
|
||||
|
||||
# Note: parser calls disable_interspersed_args(), so the result of this
|
||||
# call is to split the initial args into the general options before the
|
||||
# subcommand and everything else.
|
||||
# For example:
|
||||
# args: ['--timeout=5', 'install', '--user', 'INITools']
|
||||
# general_options: ['--timeout==5']
|
||||
# args_else: ['install', '--user', 'INITools']
|
||||
general_options, args_else = parser.parse_args(args)
|
||||
|
||||
# --version
|
||||
if general_options.version:
|
||||
sys.stdout.write(parser.version)
|
||||
sys.stdout.write(os.linesep)
|
||||
sys.exit()
|
||||
|
||||
# pip || pip help -> print_help()
|
||||
if not args_else or (args_else[0] == 'help' and len(args_else) == 1):
|
||||
parser.print_help()
|
||||
sys.exit()
|
||||
|
||||
# the subcommand name
|
||||
cmd_name = args_else[0]
|
||||
|
||||
if cmd_name not in commands_dict:
|
||||
guess = get_similar_commands(cmd_name)
|
||||
|
||||
msg = ['unknown command "%s"' % cmd_name]
|
||||
if guess:
|
||||
msg.append('maybe you meant "%s"' % guess)
|
||||
|
||||
raise CommandError(' - '.join(msg))
|
||||
|
||||
# all the args without the subcommand
|
||||
cmd_args = args[:]
|
||||
cmd_args.remove(cmd_name)
|
||||
|
||||
return cmd_name, cmd_args
|
||||
|
||||
|
||||
def check_isolated(args):
|
||||
isolated = False
|
||||
|
||||
if "--isolated" in args:
|
||||
isolated = True
|
||||
|
||||
return isolated
|
||||
|
||||
|
||||
def main(args=None):
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
@@ -293,7 +61,7 @@ def main(args=None):
|
||||
autocomplete()
|
||||
|
||||
try:
|
||||
cmd_name, cmd_args = parseopts(args)
|
||||
cmd_name, cmd_args = parse_command(args)
|
||||
except PipError as exc:
|
||||
sys.stderr.write("ERROR: %s" % exc)
|
||||
sys.stderr.write(os.linesep)
|
||||
@@ -306,5 +74,5 @@ def main(args=None):
|
||||
except locale.Error as e:
|
||||
# setlocale can apparently crash if locale are uninitialized
|
||||
logger.debug("Ignoring error %s when setting locale", e)
|
||||
command = commands_dict[cmd_name](isolated=check_isolated(cmd_args))
|
||||
command = commands_dict[cmd_name](isolated=("--isolated" in cmd_args))
|
||||
return command.main(cmd_args)
|
||||
|
||||
@@ -7,6 +7,8 @@ import sys
|
||||
from distutils.sysconfig import get_python_lib
|
||||
from sysconfig import get_paths
|
||||
|
||||
from pipenv.patched.notpip._vendor.pkg_resources import Requirement, VersionConflict, WorkingSet
|
||||
|
||||
from pipenv.patched.notpip._internal.utils.misc import call_subprocess
|
||||
from pipenv.patched.notpip._internal.utils.temp_dir import TempDirectory
|
||||
from pipenv.patched.notpip._internal.utils.ui import open_spinner
|
||||
@@ -75,6 +77,20 @@ class BuildEnvironment(object):
|
||||
def cleanup(self):
|
||||
self._temp_dir.cleanup()
|
||||
|
||||
def missing_requirements(self, reqs):
|
||||
"""Return a list of the requirements from reqs that are not present
|
||||
"""
|
||||
missing = []
|
||||
with self:
|
||||
ws = WorkingSet(os.environ["PYTHONPATH"].split(os.pathsep))
|
||||
for req in reqs:
|
||||
try:
|
||||
if ws.find(Requirement.parse(req)) is None:
|
||||
missing.append(req)
|
||||
except VersionConflict:
|
||||
missing.append(req)
|
||||
return missing
|
||||
|
||||
def install_requirements(self, finder, requirements, message):
|
||||
args = [
|
||||
sys.executable, '-m', 'pip', 'install', '--ignore-installed',
|
||||
|
||||
@@ -8,9 +8,9 @@ import os
|
||||
|
||||
from pipenv.patched.notpip._vendor.packaging.utils import canonicalize_name
|
||||
|
||||
from pipenv.patched.notpip._internal import index
|
||||
from pipenv.patched.notpip._internal.compat import expanduser
|
||||
from pipenv.patched.notpip._internal.download import path_to_url
|
||||
from pipenv.patched.notpip._internal.models.link import Link
|
||||
from pipenv.patched.notpip._internal.utils.compat import expanduser
|
||||
from pipenv.patched.notpip._internal.utils.temp_dir import TempDirectory
|
||||
from pipenv.patched.notpip._internal.wheel import InvalidWheelFilename, Wheel
|
||||
|
||||
@@ -22,7 +22,7 @@ class Cache(object):
|
||||
|
||||
|
||||
:param cache_dir: The root of the cache.
|
||||
:param format_control: A pip.index.FormatControl object to limit
|
||||
:param format_control: An object of FormatControl class to limit
|
||||
binaries being read from the cache.
|
||||
:param allowed_formats: which formats of files the cache should store.
|
||||
('binary' and 'source' are the only allowed values)
|
||||
@@ -72,8 +72,8 @@ class Cache(object):
|
||||
return []
|
||||
|
||||
canonical_name = canonicalize_name(package_name)
|
||||
formats = index.fmt_ctl_formats(
|
||||
self.format_control, canonical_name
|
||||
formats = self.format_control.get_allowed_formats(
|
||||
canonical_name
|
||||
)
|
||||
if not self.allowed_formats.intersection(formats):
|
||||
return []
|
||||
@@ -101,7 +101,7 @@ class Cache(object):
|
||||
root = self.get_path_for_link(link)
|
||||
path = os.path.join(root, candidate)
|
||||
|
||||
return index.Link(path_to_url(path))
|
||||
return Link(path_to_url(path))
|
||||
|
||||
def cleanup(self):
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
"""Subpackage containing all of pip's command line interface related code
|
||||
"""
|
||||
|
||||
# This file intentionally does not import submodules
|
||||
@@ -0,0 +1,152 @@
|
||||
"""Logic that powers autocompletion installed by ``pip completion``.
|
||||
"""
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pipenv.patched.notpip._internal.cli.main_parser import create_main_parser
|
||||
from pipenv.patched.notpip._internal.commands import commands_dict, get_summaries
|
||||
from pipenv.patched.notpip._internal.utils.misc import get_installed_distributions
|
||||
|
||||
|
||||
def autocomplete():
|
||||
"""Entry Point for completion of main and subcommand options.
|
||||
"""
|
||||
# Don't complete if user hasn't sourced bash_completion file.
|
||||
if 'PIP_AUTO_COMPLETE' not in os.environ:
|
||||
return
|
||||
cwords = os.environ['COMP_WORDS'].split()[1:]
|
||||
cword = int(os.environ['COMP_CWORD'])
|
||||
try:
|
||||
current = cwords[cword - 1]
|
||||
except IndexError:
|
||||
current = ''
|
||||
|
||||
subcommands = [cmd for cmd, summary in get_summaries()]
|
||||
options = []
|
||||
# subcommand
|
||||
try:
|
||||
subcommand_name = [w for w in cwords if w in subcommands][0]
|
||||
except IndexError:
|
||||
subcommand_name = None
|
||||
|
||||
parser = create_main_parser()
|
||||
# subcommand options
|
||||
if subcommand_name:
|
||||
# special case: 'help' subcommand has no options
|
||||
if subcommand_name == 'help':
|
||||
sys.exit(1)
|
||||
# special case: list locally installed dists for show and uninstall
|
||||
should_list_installed = (
|
||||
subcommand_name in ['show', 'uninstall'] and
|
||||
not current.startswith('-')
|
||||
)
|
||||
if should_list_installed:
|
||||
installed = []
|
||||
lc = current.lower()
|
||||
for dist in get_installed_distributions(local_only=True):
|
||||
if dist.key.startswith(lc) and dist.key not in cwords[1:]:
|
||||
installed.append(dist.key)
|
||||
# if there are no dists installed, fall back to option completion
|
||||
if installed:
|
||||
for dist in installed:
|
||||
print(dist)
|
||||
sys.exit(1)
|
||||
|
||||
subcommand = commands_dict[subcommand_name]()
|
||||
|
||||
for opt in subcommand.parser.option_list_all:
|
||||
if opt.help != optparse.SUPPRESS_HELP:
|
||||
for opt_str in opt._long_opts + opt._short_opts:
|
||||
options.append((opt_str, opt.nargs))
|
||||
|
||||
# filter out previously specified options from available options
|
||||
prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]]
|
||||
options = [(x, v) for (x, v) in options if x not in prev_opts]
|
||||
# filter options by current input
|
||||
options = [(k, v) for k, v in options if k.startswith(current)]
|
||||
# get completion type given cwords and available subcommand options
|
||||
completion_type = get_path_completion_type(
|
||||
cwords, cword, subcommand.parser.option_list_all,
|
||||
)
|
||||
# get completion files and directories if ``completion_type`` is
|
||||
# ``<file>``, ``<dir>`` or ``<path>``
|
||||
if completion_type:
|
||||
options = auto_complete_paths(current, completion_type)
|
||||
options = ((opt, 0) for opt in options)
|
||||
for option in options:
|
||||
opt_label = option[0]
|
||||
# append '=' to options which require args
|
||||
if option[1] and option[0][:2] == "--":
|
||||
opt_label += '='
|
||||
print(opt_label)
|
||||
else:
|
||||
# show main parser options only when necessary
|
||||
|
||||
opts = [i.option_list for i in parser.option_groups]
|
||||
opts.append(parser.option_list)
|
||||
opts = (o for it in opts for o in it)
|
||||
if current.startswith('-'):
|
||||
for opt in opts:
|
||||
if opt.help != optparse.SUPPRESS_HELP:
|
||||
subcommands += opt._long_opts + opt._short_opts
|
||||
else:
|
||||
# get completion type given cwords and all available options
|
||||
completion_type = get_path_completion_type(cwords, cword, opts)
|
||||
if completion_type:
|
||||
subcommands = auto_complete_paths(current, completion_type)
|
||||
|
||||
print(' '.join([x for x in subcommands if x.startswith(current)]))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_path_completion_type(cwords, cword, opts):
|
||||
"""Get the type of path completion (``file``, ``dir``, ``path`` or None)
|
||||
|
||||
:param cwords: same as the environmental variable ``COMP_WORDS``
|
||||
:param cword: same as the environmental variable ``COMP_CWORD``
|
||||
:param opts: The available options to check
|
||||
:return: path completion type (``file``, ``dir``, ``path`` or None)
|
||||
"""
|
||||
if cword < 2 or not cwords[cword - 2].startswith('-'):
|
||||
return
|
||||
for opt in opts:
|
||||
if opt.help == optparse.SUPPRESS_HELP:
|
||||
continue
|
||||
for o in str(opt).split('/'):
|
||||
if cwords[cword - 2].split('=')[0] == o:
|
||||
if not opt.metavar or any(
|
||||
x in ('path', 'file', 'dir')
|
||||
for x in opt.metavar.split('/')):
|
||||
return opt.metavar
|
||||
|
||||
|
||||
def auto_complete_paths(current, completion_type):
|
||||
"""If ``completion_type`` is ``file`` or ``path``, list all regular files
|
||||
and directories starting with ``current``; otherwise only list directories
|
||||
starting with ``current``.
|
||||
|
||||
:param current: The word to be completed
|
||||
:param completion_type: path completion type(`file`, `path` or `dir`)i
|
||||
:return: A generator of regular files and/or directories
|
||||
"""
|
||||
directory, filename = os.path.split(current)
|
||||
current_path = os.path.abspath(directory)
|
||||
# Don't complete paths if they can't be accessed
|
||||
if not os.access(current_path, os.R_OK):
|
||||
return
|
||||
filename = os.path.normcase(filename)
|
||||
# list all files that start with ``filename``
|
||||
file_list = (x for x in os.listdir(current_path)
|
||||
if os.path.normcase(x).startswith(filename))
|
||||
for f in file_list:
|
||||
opt = os.path.join(current_path, f)
|
||||
comp_file = os.path.normcase(os.path.join(directory, f))
|
||||
# complete regular files when there is not ``<dir>`` after option
|
||||
# complete directories when there is ``<file>``, ``<path>`` or
|
||||
# ``<dir>``after option
|
||||
if completion_type != 'dir' and os.path.isfile(opt):
|
||||
yield comp_file
|
||||
elif os.path.isdir(opt):
|
||||
yield os.path.join(comp_file, '')
|
||||
+18
-14
@@ -7,10 +7,14 @@ import optparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pipenv.patched.notpip._internal import cmdoptions
|
||||
from pipenv.patched.notpip._internal.baseparser import (
|
||||
from pipenv.patched.notpip._internal.cli import cmdoptions
|
||||
from pipenv.patched.notpip._internal.cli.parser import (
|
||||
ConfigOptionParser, UpdatingDefaultsHelpFormatter,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.cli.status_codes import (
|
||||
ERROR, PREVIOUS_BUILD_DIR_ERROR, SUCCESS, UNKNOWN_ERROR,
|
||||
VIRTUALENV_NOT_FOUND,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.download import PipSession
|
||||
from pipenv.patched.notpip._internal.exceptions import (
|
||||
BadCommand, CommandError, InstallationError, PreviousBuildDirError,
|
||||
@@ -18,12 +22,10 @@ from pipenv.patched.notpip._internal.exceptions import (
|
||||
)
|
||||
from pipenv.patched.notpip._internal.index import PackageFinder
|
||||
from pipenv.patched.notpip._internal.locations import running_under_virtualenv
|
||||
from pipenv.patched.notpip._internal.req.req_file import parse_requirements
|
||||
from pipenv.patched.notpip._internal.req.req_install import InstallRequirement
|
||||
from pipenv.patched.notpip._internal.status_codes import (
|
||||
ERROR, PREVIOUS_BUILD_DIR_ERROR, SUCCESS, UNKNOWN_ERROR,
|
||||
VIRTUALENV_NOT_FOUND,
|
||||
from pipenv.patched.notpip._internal.req.constructors import (
|
||||
install_req_from_editable, install_req_from_line,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.req.req_file import parse_requirements
|
||||
from pipenv.patched.notpip._internal.utils.logging import setup_logging
|
||||
from pipenv.patched.notpip._internal.utils.misc import get_prog, normalize_path
|
||||
from pipenv.patched.notpip._internal.utils.outdated import pip_version_check
|
||||
@@ -168,12 +170,14 @@ class Command(object):
|
||||
|
||||
return UNKNOWN_ERROR
|
||||
finally:
|
||||
# Check if we're using the latest version of pip available
|
||||
skip_version_check = (
|
||||
options.disable_pip_version_check or
|
||||
getattr(options, "no_index", False)
|
||||
allow_version_check = (
|
||||
# Does this command have the index_group options?
|
||||
hasattr(options, "no_index") and
|
||||
# Is this command allowed to perform this check?
|
||||
not (options.disable_pip_version_check or options.no_index)
|
||||
)
|
||||
if not skip_version_check:
|
||||
# Check if we're using the latest version of pip available
|
||||
if allow_version_check:
|
||||
session = self._build_session(
|
||||
options,
|
||||
retries=0,
|
||||
@@ -208,7 +212,7 @@ class RequirementCommand(Command):
|
||||
requirement_set.add_requirement(req_to_add)
|
||||
|
||||
for req in args:
|
||||
req_to_add = InstallRequirement.from_line(
|
||||
req_to_add = install_req_from_line(
|
||||
req, None, isolated=options.isolated_mode,
|
||||
wheel_cache=wheel_cache
|
||||
)
|
||||
@@ -216,7 +220,7 @@ class RequirementCommand(Command):
|
||||
requirement_set.add_requirement(req_to_add)
|
||||
|
||||
for req in options.editables:
|
||||
req_to_add = InstallRequirement.from_editable(
|
||||
req_to_add = install_req_from_editable(
|
||||
req,
|
||||
isolated=options.isolated_mode,
|
||||
wheel_cache=wheel_cache
|
||||
+106
-11
@@ -13,10 +13,9 @@ import warnings
|
||||
from functools import partial
|
||||
from optparse import SUPPRESS_HELP, Option, OptionGroup
|
||||
|
||||
from pipenv.patched.notpip._internal.index import (
|
||||
FormatControl, fmt_ctl_handle_mutual_exclude, fmt_ctl_no_binary,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.exceptions import CommandError
|
||||
from pipenv.patched.notpip._internal.locations import USER_CACHE_DIR, src_prefix
|
||||
from pipenv.patched.notpip._internal.models.format_control import FormatControl
|
||||
from pipenv.patched.notpip._internal.models.index import PyPI
|
||||
from pipenv.patched.notpip._internal.utils.hashes import STRONG_HASHES
|
||||
from pipenv.patched.notpip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
@@ -53,13 +52,52 @@ def check_install_build_global(options, check_options=None):
|
||||
names = ["build_options", "global_options", "install_options"]
|
||||
if any(map(getname, names)):
|
||||
control = options.format_control
|
||||
fmt_ctl_no_binary(control)
|
||||
control.disallow_binaries()
|
||||
warnings.warn(
|
||||
'Disabling all use of wheels due to the use of --build-options '
|
||||
'/ --global-options / --install-options.', stacklevel=2,
|
||||
)
|
||||
|
||||
|
||||
def check_dist_restriction(options, check_target=False):
|
||||
"""Function for determining if custom platform options are allowed.
|
||||
|
||||
:param options: The OptionParser options.
|
||||
:param check_target: Whether or not to check if --target is being used.
|
||||
"""
|
||||
dist_restriction_set = any([
|
||||
options.python_version,
|
||||
options.platform,
|
||||
options.abi,
|
||||
options.implementation,
|
||||
])
|
||||
|
||||
binary_only = FormatControl(set(), {':all:'})
|
||||
sdist_dependencies_allowed = (
|
||||
options.format_control != binary_only and
|
||||
not options.ignore_dependencies
|
||||
)
|
||||
|
||||
# Installations or downloads using dist restrictions must not combine
|
||||
# source distributions and dist-specific wheels, as they are not
|
||||
# gauranteed to be locally compatible.
|
||||
if dist_restriction_set and sdist_dependencies_allowed:
|
||||
raise CommandError(
|
||||
"When restricting platform and interpreter constraints using "
|
||||
"--python-version, --platform, --abi, or --implementation, "
|
||||
"either --no-deps must be set, or --only-binary=:all: must be "
|
||||
"set and --no-binary must not be set (or must be set to "
|
||||
":none:)."
|
||||
)
|
||||
|
||||
if check_target:
|
||||
if dist_restriction_set and not options.target_dir:
|
||||
raise CommandError(
|
||||
"Can not use any platform or abi specific options unless "
|
||||
"installing via '--target'"
|
||||
)
|
||||
|
||||
|
||||
###########
|
||||
# options #
|
||||
###########
|
||||
@@ -365,24 +403,25 @@ def _get_format_control(values, option):
|
||||
|
||||
|
||||
def _handle_no_binary(option, opt_str, value, parser):
|
||||
existing = getattr(parser.values, option.dest)
|
||||
fmt_ctl_handle_mutual_exclude(
|
||||
existing = _get_format_control(parser.values, option)
|
||||
FormatControl.handle_mutual_excludes(
|
||||
value, existing.no_binary, existing.only_binary,
|
||||
)
|
||||
|
||||
|
||||
def _handle_only_binary(option, opt_str, value, parser):
|
||||
existing = getattr(parser.values, option.dest)
|
||||
fmt_ctl_handle_mutual_exclude(
|
||||
existing = _get_format_control(parser.values, option)
|
||||
FormatControl.handle_mutual_excludes(
|
||||
value, existing.only_binary, existing.no_binary,
|
||||
)
|
||||
|
||||
|
||||
def no_binary():
|
||||
format_control = FormatControl(set(), set())
|
||||
return Option(
|
||||
"--no-binary", dest="format_control", action="callback",
|
||||
callback=_handle_no_binary, type="str",
|
||||
default=FormatControl(set(), set()),
|
||||
default=format_control,
|
||||
help="Do not use binary packages. Can be supplied multiple times, and "
|
||||
"each time adds to the existing value. Accepts either :all: to "
|
||||
"disable all binary packages, :none: to empty the set, or one or "
|
||||
@@ -393,10 +432,11 @@ def no_binary():
|
||||
|
||||
|
||||
def only_binary():
|
||||
format_control = FormatControl(set(), set())
|
||||
return Option(
|
||||
"--only-binary", dest="format_control", action="callback",
|
||||
callback=_handle_only_binary, type="str",
|
||||
default=FormatControl(set(), set()),
|
||||
default=format_control,
|
||||
help="Do not use source packages. Can be supplied multiple times, and "
|
||||
"each time adds to the existing value. Accepts either :all: to "
|
||||
"disable all source packages, :none: to empty the set, or one or "
|
||||
@@ -406,6 +446,61 @@ def only_binary():
|
||||
)
|
||||
|
||||
|
||||
platform = partial(
|
||||
Option,
|
||||
'--platform',
|
||||
dest='platform',
|
||||
metavar='platform',
|
||||
default=None,
|
||||
help=("Only use wheels compatible with <platform>. "
|
||||
"Defaults to the platform of the running system."),
|
||||
)
|
||||
|
||||
|
||||
python_version = partial(
|
||||
Option,
|
||||
'--python-version',
|
||||
dest='python_version',
|
||||
metavar='python_version',
|
||||
default=None,
|
||||
help=("Only use wheels compatible with Python "
|
||||
"interpreter version <version>. If not specified, then the "
|
||||
"current system interpreter minor version is used. A major "
|
||||
"version (e.g. '2') can be specified to match all "
|
||||
"minor revs of that major version. A minor version "
|
||||
"(e.g. '34') can also be specified."),
|
||||
)
|
||||
|
||||
|
||||
implementation = partial(
|
||||
Option,
|
||||
'--implementation',
|
||||
dest='implementation',
|
||||
metavar='implementation',
|
||||
default=None,
|
||||
help=("Only use wheels compatible with Python "
|
||||
"implementation <implementation>, e.g. 'pp', 'jy', 'cp', "
|
||||
" or 'ip'. If not specified, then the current "
|
||||
"interpreter implementation is used. Use 'py' to force "
|
||||
"implementation-agnostic wheels."),
|
||||
)
|
||||
|
||||
|
||||
abi = partial(
|
||||
Option,
|
||||
'--abi',
|
||||
dest='abi',
|
||||
metavar='abi',
|
||||
default=None,
|
||||
help=("Only use wheels compatible with Python "
|
||||
"abi <abi>, e.g. 'pypy_41'. If not specified, then the "
|
||||
"current interpreter abi tag is used. Generally "
|
||||
"you will need to specify --implementation, "
|
||||
"--platform, and --python-version when using "
|
||||
"this option."),
|
||||
)
|
||||
|
||||
|
||||
def prefer_binary():
|
||||
return Option(
|
||||
"--prefer-binary",
|
||||
@@ -501,7 +596,7 @@ no_clean = partial(
|
||||
'--no-clean',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help="Don't clean up build directories)."
|
||||
help="Don't clean up build directories."
|
||||
) # type: Any
|
||||
|
||||
pre = partial(
|
||||
@@ -0,0 +1,96 @@
|
||||
"""A single place for constructing and exposing the main parser
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pipenv.patched.notpip import __version__
|
||||
from pipenv.patched.notpip._internal.cli import cmdoptions
|
||||
from pipenv.patched.notpip._internal.cli.parser import (
|
||||
ConfigOptionParser, UpdatingDefaultsHelpFormatter,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.commands import (
|
||||
commands_dict, get_similar_commands, get_summaries,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.exceptions import CommandError
|
||||
from pipenv.patched.notpip._internal.utils.misc import get_prog
|
||||
|
||||
__all__ = ["create_main_parser", "parse_command"]
|
||||
|
||||
|
||||
def create_main_parser():
|
||||
"""Creates and returns the main parser for pip's CLI
|
||||
"""
|
||||
|
||||
parser_kw = {
|
||||
'usage': '\n%prog <command> [options]',
|
||||
'add_help_option': False,
|
||||
'formatter': UpdatingDefaultsHelpFormatter(),
|
||||
'name': 'global',
|
||||
'prog': get_prog(),
|
||||
}
|
||||
|
||||
parser = ConfigOptionParser(**parser_kw)
|
||||
parser.disable_interspersed_args()
|
||||
|
||||
pip_pkg_dir = os.path.abspath(os.path.join(
|
||||
os.path.dirname(__file__), "..", "..",
|
||||
))
|
||||
parser.version = 'pip %s from %s (python %s)' % (
|
||||
__version__, pip_pkg_dir, sys.version[:3],
|
||||
)
|
||||
|
||||
# add the general options
|
||||
gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser)
|
||||
parser.add_option_group(gen_opts)
|
||||
|
||||
parser.main = True # so the help formatter knows
|
||||
|
||||
# create command listing for description
|
||||
command_summaries = get_summaries()
|
||||
description = [''] + ['%-27s %s' % (i, j) for i, j in command_summaries]
|
||||
parser.description = '\n'.join(description)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def parse_command(args):
|
||||
parser = create_main_parser()
|
||||
|
||||
# Note: parser calls disable_interspersed_args(), so the result of this
|
||||
# call is to split the initial args into the general options before the
|
||||
# subcommand and everything else.
|
||||
# For example:
|
||||
# args: ['--timeout=5', 'install', '--user', 'INITools']
|
||||
# general_options: ['--timeout==5']
|
||||
# args_else: ['install', '--user', 'INITools']
|
||||
general_options, args_else = parser.parse_args(args)
|
||||
|
||||
# --version
|
||||
if general_options.version:
|
||||
sys.stdout.write(parser.version)
|
||||
sys.stdout.write(os.linesep)
|
||||
sys.exit()
|
||||
|
||||
# pip || pip help -> print_help()
|
||||
if not args_else or (args_else[0] == 'help' and len(args_else) == 1):
|
||||
parser.print_help()
|
||||
sys.exit()
|
||||
|
||||
# the subcommand name
|
||||
cmd_name = args_else[0]
|
||||
|
||||
if cmd_name not in commands_dict:
|
||||
guess = get_similar_commands(cmd_name)
|
||||
|
||||
msg = ['unknown command "%s"' % cmd_name]
|
||||
if guess:
|
||||
msg.append('maybe you meant "%s"' % guess)
|
||||
|
||||
raise CommandError(' - '.join(msg))
|
||||
|
||||
# all the args without the subcommand
|
||||
cmd_args = args[:]
|
||||
cmd_args.remove(cmd_name)
|
||||
|
||||
return cmd_name, cmd_args
|
||||
+25
-4
@@ -9,8 +9,9 @@ from distutils.util import strtobool
|
||||
|
||||
from pipenv.patched.notpip._vendor.six import string_types
|
||||
|
||||
from pipenv.patched.notpip._internal.compat import get_terminal_size
|
||||
from pipenv.patched.notpip._internal.cli.status_codes import UNKNOWN_ERROR
|
||||
from pipenv.patched.notpip._internal.configuration import Configuration, ConfigurationError
|
||||
from pipenv.patched.notpip._internal.utils.compat import get_terminal_size
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -192,7 +193,14 @@ class ConfigOptionParser(CustomOptionParser):
|
||||
continue
|
||||
|
||||
if option.action in ('store_true', 'store_false', 'count'):
|
||||
val = strtobool(val)
|
||||
try:
|
||||
val = strtobool(val)
|
||||
except ValueError:
|
||||
error_msg = invalid_config_error_message(
|
||||
option.action, key, val
|
||||
)
|
||||
self.error(error_msg)
|
||||
|
||||
elif option.action == 'append':
|
||||
val = val.split()
|
||||
val = [self.check_default(option, key, v) for v in val]
|
||||
@@ -225,7 +233,7 @@ class ConfigOptionParser(CustomOptionParser):
|
||||
try:
|
||||
self.config.load()
|
||||
except ConfigurationError as err:
|
||||
self.exit(2, err.args[0])
|
||||
self.exit(UNKNOWN_ERROR, str(err))
|
||||
|
||||
defaults = self._update_defaults(self.defaults.copy()) # ours
|
||||
for option in self._get_all_options():
|
||||
@@ -237,4 +245,17 @@ class ConfigOptionParser(CustomOptionParser):
|
||||
|
||||
def error(self, msg):
|
||||
self.print_usage(sys.stderr)
|
||||
self.exit(2, "%s\n" % msg)
|
||||
self.exit(UNKNOWN_ERROR, "%s\n" % msg)
|
||||
|
||||
|
||||
def invalid_config_error_message(action, key, val):
|
||||
"""Returns a better error message when invalid configuration option
|
||||
is provided."""
|
||||
if action in ('store_true', 'store_false'):
|
||||
return ("{0} is not a valid value for {1} option, "
|
||||
"please specify a boolean value like yes/no, "
|
||||
"true/false or 1/0 instead.").format(val, key)
|
||||
|
||||
return ("{0} is not a valid value for {1} option, "
|
||||
"please specify a numerical value like 1/0 "
|
||||
"instead.").format(val, key)
|
||||
@@ -21,7 +21,7 @@ from pipenv.patched.notpip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import List, Type # noqa: F401
|
||||
from pipenv.patched.notpip._internal.basecommand import Command # noqa: F401
|
||||
from pipenv.patched.notpip._internal.cli.base_command import Command # noqa: F401
|
||||
|
||||
commands_order = [
|
||||
InstallCommand,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
|
||||
from pipenv.patched.notpip._internal.basecommand import Command
|
||||
from pipenv.patched.notpip._internal.cli.base_command import Command
|
||||
from pipenv.patched.notpip._internal.operations.check import (
|
||||
check_package_set, create_package_set_from_installed,
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import absolute_import
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
from pipenv.patched.notpip._internal.basecommand import Command
|
||||
from pipenv.patched.notpip._internal.cli.base_command import Command
|
||||
from pipenv.patched.notpip._internal.utils.misc import get_prog
|
||||
|
||||
BASE_COMPLETION = """
|
||||
|
||||
@@ -2,11 +2,11 @@ import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from pipenv.patched.notpip._internal.basecommand import Command
|
||||
from pipenv.patched.notpip._internal.cli.base_command import Command
|
||||
from pipenv.patched.notpip._internal.cli.status_codes import ERROR, SUCCESS
|
||||
from pipenv.patched.notpip._internal.configuration import Configuration, kinds
|
||||
from pipenv.patched.notpip._internal.exceptions import PipError
|
||||
from pipenv.patched.notpip._internal.locations import venv_config_file
|
||||
from pipenv.patched.notpip._internal.status_codes import ERROR, SUCCESS
|
||||
from pipenv.patched.notpip._internal.utils.misc import get_prog
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -3,10 +3,8 @@ from __future__ import absolute_import
|
||||
import logging
|
||||
import os
|
||||
|
||||
from pipenv.patched.notpip._internal import cmdoptions
|
||||
from pipenv.patched.notpip._internal.basecommand import RequirementCommand
|
||||
from pipenv.patched.notpip._internal.exceptions import CommandError
|
||||
from pipenv.patched.notpip._internal.index import FormatControl
|
||||
from pipenv.patched.notpip._internal.cli import cmdoptions
|
||||
from pipenv.patched.notpip._internal.cli.base_command import RequirementCommand
|
||||
from pipenv.patched.notpip._internal.operations.prepare import RequirementPreparer
|
||||
from pipenv.patched.notpip._internal.req import RequirementSet
|
||||
from pipenv.patched.notpip._internal.req.req_tracker import RequirementTracker
|
||||
@@ -69,52 +67,10 @@ class DownloadCommand(RequirementCommand):
|
||||
help=("Download packages into <dir>."),
|
||||
)
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--platform',
|
||||
dest='platform',
|
||||
metavar='platform',
|
||||
default=None,
|
||||
help=("Only download wheels compatible with <platform>. "
|
||||
"Defaults to the platform of the running system."),
|
||||
)
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--python-version',
|
||||
dest='python_version',
|
||||
metavar='python_version',
|
||||
default=None,
|
||||
help=("Only download wheels compatible with Python "
|
||||
"interpreter version <version>. If not specified, then the "
|
||||
"current system interpreter minor version is used. A major "
|
||||
"version (e.g. '2') can be specified to match all "
|
||||
"minor revs of that major version. A minor version "
|
||||
"(e.g. '34') can also be specified."),
|
||||
)
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--implementation',
|
||||
dest='implementation',
|
||||
metavar='implementation',
|
||||
default=None,
|
||||
help=("Only download wheels compatible with Python "
|
||||
"implementation <implementation>, e.g. 'pp', 'jy', 'cp', "
|
||||
" or 'ip'. If not specified, then the current "
|
||||
"interpreter implementation is used. Use 'py' to force "
|
||||
"implementation-agnostic wheels."),
|
||||
)
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--abi',
|
||||
dest='abi',
|
||||
metavar='abi',
|
||||
default=None,
|
||||
help=("Only download wheels compatible with Python "
|
||||
"abi <abi>, e.g. 'pypy_41'. If not specified, then the "
|
||||
"current interpreter abi tag is used. Generally "
|
||||
"you will need to specify --implementation, "
|
||||
"--platform, and --python-version when using "
|
||||
"this option."),
|
||||
)
|
||||
cmd_opts.add_option(cmdoptions.platform())
|
||||
cmd_opts.add_option(cmdoptions.python_version())
|
||||
cmd_opts.add_option(cmdoptions.implementation())
|
||||
cmd_opts.add_option(cmdoptions.abi())
|
||||
|
||||
index_opts = cmdoptions.make_option_group(
|
||||
cmdoptions.index_group,
|
||||
@@ -135,25 +91,7 @@ class DownloadCommand(RequirementCommand):
|
||||
else:
|
||||
python_versions = None
|
||||
|
||||
dist_restriction_set = any([
|
||||
options.python_version,
|
||||
options.platform,
|
||||
options.abi,
|
||||
options.implementation,
|
||||
])
|
||||
binary_only = FormatControl(set(), {':all:'})
|
||||
no_sdist_dependencies = (
|
||||
options.format_control != binary_only and
|
||||
not options.ignore_dependencies
|
||||
)
|
||||
if dist_restriction_set and no_sdist_dependencies:
|
||||
raise CommandError(
|
||||
"When restricting platform and interpreter constraints using "
|
||||
"--python-version, --platform, --abi, or --implementation, "
|
||||
"either --no-deps must be set, or --only-binary=:all: must be "
|
||||
"set and --no-binary must not be set (or must be set to "
|
||||
":none:)."
|
||||
)
|
||||
cmdoptions.check_dist_restriction(options)
|
||||
|
||||
options.src_dir = os.path.abspath(options.src_dir)
|
||||
options.download_dir = normalize_path(options.download_dir)
|
||||
|
||||
@@ -2,11 +2,11 @@ from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
|
||||
from pipenv.patched.notpip._internal import index
|
||||
from pipenv.patched.notpip._internal.basecommand import Command
|
||||
from pipenv.patched.notpip._internal.cache import WheelCache
|
||||
from pipenv.patched.notpip._internal.compat import stdlib_pkgs
|
||||
from pipenv.patched.notpip._internal.cli.base_command import Command
|
||||
from pipenv.patched.notpip._internal.models.format_control import FormatControl
|
||||
from pipenv.patched.notpip._internal.operations.freeze import freeze
|
||||
from pipenv.patched.notpip._internal.utils.compat import stdlib_pkgs
|
||||
|
||||
DEV_PKGS = {'pip', 'setuptools', 'distribute', 'wheel'}
|
||||
|
||||
@@ -71,7 +71,7 @@ class FreezeCommand(Command):
|
||||
self.parser.insert_option_group(0, self.cmd_opts)
|
||||
|
||||
def run(self, options, args):
|
||||
format_control = index.FormatControl(set(), set())
|
||||
format_control = FormatControl(set(), set())
|
||||
wheel_cache = WheelCache(options.cache_dir, format_control)
|
||||
skip = set(stdlib_pkgs)
|
||||
if not options.freeze_all:
|
||||
|
||||
@@ -4,8 +4,8 @@ import hashlib
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from pipenv.patched.notpip._internal.basecommand import Command
|
||||
from pipenv.patched.notpip._internal.status_codes import ERROR
|
||||
from pipenv.patched.notpip._internal.cli.base_command import Command
|
||||
from pipenv.patched.notpip._internal.cli.status_codes import ERROR
|
||||
from pipenv.patched.notpip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES
|
||||
from pipenv.patched.notpip._internal.utils.misc import read_chunks
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from pipenv.patched.notpip._internal.basecommand import SUCCESS, Command
|
||||
from pipenv.patched.notpip._internal.cli.base_command import Command
|
||||
from pipenv.patched.notpip._internal.cli.status_codes import SUCCESS
|
||||
from pipenv.patched.notpip._internal.exceptions import CommandError
|
||||
|
||||
|
||||
|
||||
@@ -9,9 +9,10 @@ from optparse import SUPPRESS_HELP
|
||||
|
||||
from pipenv.patched.notpip._vendor import pkg_resources
|
||||
|
||||
from pipenv.patched.notpip._internal import cmdoptions
|
||||
from pipenv.patched.notpip._internal.basecommand import RequirementCommand
|
||||
from pipenv.patched.notpip._internal.cache import WheelCache
|
||||
from pipenv.patched.notpip._internal.cli import cmdoptions
|
||||
from pipenv.patched.notpip._internal.cli.base_command import RequirementCommand
|
||||
from pipenv.patched.notpip._internal.cli.status_codes import ERROR
|
||||
from pipenv.patched.notpip._internal.exceptions import (
|
||||
CommandError, InstallationError, PreviousBuildDirError,
|
||||
)
|
||||
@@ -21,7 +22,6 @@ from pipenv.patched.notpip._internal.operations.prepare import RequirementPrepar
|
||||
from pipenv.patched.notpip._internal.req import RequirementSet, install_given_reqs
|
||||
from pipenv.patched.notpip._internal.req.req_tracker import RequirementTracker
|
||||
from pipenv.patched.notpip._internal.resolve import Resolver
|
||||
from pipenv.patched.notpip._internal.status_codes import ERROR
|
||||
from pipenv.patched.notpip._internal.utils.filesystem import check_path_owner
|
||||
from pipenv.patched.notpip._internal.utils.misc import (
|
||||
ensure_dir, get_installed_version,
|
||||
@@ -83,6 +83,11 @@ class InstallCommand(RequirementCommand):
|
||||
'<dir>. Use --upgrade to replace existing packages in <dir> '
|
||||
'with new versions.'
|
||||
)
|
||||
cmd_opts.add_option(cmdoptions.platform())
|
||||
cmd_opts.add_option(cmdoptions.python_version())
|
||||
cmd_opts.add_option(cmdoptions.implementation())
|
||||
cmd_opts.add_option(cmdoptions.abi())
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--user',
|
||||
dest='use_user_site',
|
||||
@@ -204,7 +209,6 @@ class InstallCommand(RequirementCommand):
|
||||
|
||||
def run(self, options, args):
|
||||
cmdoptions.check_install_build_global(options)
|
||||
|
||||
upgrade_strategy = "to-satisfy-only"
|
||||
if options.upgrade:
|
||||
upgrade_strategy = options.upgrade_strategy
|
||||
@@ -212,6 +216,13 @@ class InstallCommand(RequirementCommand):
|
||||
if options.build_dir:
|
||||
options.build_dir = os.path.abspath(options.build_dir)
|
||||
|
||||
cmdoptions.check_dist_restriction(options, check_target=True)
|
||||
|
||||
if options.python_version:
|
||||
python_versions = [options.python_version]
|
||||
else:
|
||||
python_versions = None
|
||||
|
||||
options.src_dir = os.path.abspath(options.src_dir)
|
||||
install_options = options.install_options or []
|
||||
if options.use_user_site:
|
||||
@@ -246,7 +257,14 @@ class InstallCommand(RequirementCommand):
|
||||
global_options = options.global_options or []
|
||||
|
||||
with self._build_session(options) as session:
|
||||
finder = self._build_package_finder(options, session)
|
||||
finder = self._build_package_finder(
|
||||
options=options,
|
||||
session=session,
|
||||
platform=options.platform,
|
||||
python_versions=python_versions,
|
||||
abi=options.abi,
|
||||
implementation=options.implementation,
|
||||
)
|
||||
build_delete = (not (options.no_clean or options.build_dir))
|
||||
wheel_cache = WheelCache(options.cache_dir, options.format_control)
|
||||
|
||||
@@ -266,6 +284,7 @@ class InstallCommand(RequirementCommand):
|
||||
) as directory:
|
||||
requirement_set = RequirementSet(
|
||||
require_hashes=options.require_hashes,
|
||||
check_supported_wheels=not options.target_dir,
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
@@ -6,8 +6,8 @@ import logging
|
||||
from pipenv.patched.notpip._vendor import six
|
||||
from pipenv.patched.notpip._vendor.six.moves import zip_longest
|
||||
|
||||
from pipenv.patched.notpip._internal.basecommand import Command
|
||||
from pipenv.patched.notpip._internal.cmdoptions import index_group, make_option_group
|
||||
from pipenv.patched.notpip._internal.cli import cmdoptions
|
||||
from pipenv.patched.notpip._internal.cli.base_command import Command
|
||||
from pipenv.patched.notpip._internal.exceptions import CommandError
|
||||
from pipenv.patched.notpip._internal.index import PackageFinder
|
||||
from pipenv.patched.notpip._internal.utils.misc import (
|
||||
@@ -102,7 +102,9 @@ class ListCommand(Command):
|
||||
help='Include editable package from output.',
|
||||
default=True,
|
||||
)
|
||||
index_opts = make_option_group(index_group, self.parser)
|
||||
index_opts = cmdoptions.make_option_group(
|
||||
cmdoptions.index_group, self.parser
|
||||
)
|
||||
|
||||
self.parser.insert_option_group(0, index_opts)
|
||||
self.parser.insert_option_group(0, cmd_opts)
|
||||
|
||||
@@ -11,12 +11,12 @@ from pipenv.patched.notpip._vendor.packaging.version import parse as parse_versi
|
||||
# why we ignore the type on this import
|
||||
from pipenv.patched.notpip._vendor.six.moves import xmlrpc_client # type: ignore
|
||||
|
||||
from pipenv.patched.notpip._internal.basecommand import SUCCESS, Command
|
||||
from pipenv.patched.notpip._internal.compat import get_terminal_size
|
||||
from pipenv.patched.notpip._internal.cli.base_command import Command
|
||||
from pipenv.patched.notpip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS
|
||||
from pipenv.patched.notpip._internal.download import PipXmlrpcTransport
|
||||
from pipenv.patched.notpip._internal.exceptions import CommandError
|
||||
from pipenv.patched.notpip._internal.models.index import PyPI
|
||||
from pipenv.patched.notpip._internal.status_codes import NO_MATCHES_FOUND
|
||||
from pipenv.patched.notpip._internal.utils.compat import get_terminal_size
|
||||
from pipenv.patched.notpip._internal.utils.logging import indent_log
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -7,8 +7,8 @@ from email.parser import FeedParser # type: ignore
|
||||
from pipenv.patched.notpip._vendor import pkg_resources
|
||||
from pipenv.patched.notpip._vendor.packaging.utils import canonicalize_name
|
||||
|
||||
from pipenv.patched.notpip._internal.basecommand import Command
|
||||
from pipenv.patched.notpip._internal.status_codes import ERROR, SUCCESS
|
||||
from pipenv.patched.notpip._internal.cli.base_command import Command
|
||||
from pipenv.patched.notpip._internal.cli.status_codes import ERROR, SUCCESS
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ from __future__ import absolute_import
|
||||
|
||||
from pipenv.patched.notpip._vendor.packaging.utils import canonicalize_name
|
||||
|
||||
from pipenv.patched.notpip._internal.basecommand import Command
|
||||
from pipenv.patched.notpip._internal.cli.base_command import Command
|
||||
from pipenv.patched.notpip._internal.exceptions import InstallationError
|
||||
from pipenv.patched.notpip._internal.req import InstallRequirement, parse_requirements
|
||||
from pipenv.patched.notpip._internal.req import parse_requirements
|
||||
from pipenv.patched.notpip._internal.req.constructors import install_req_from_line
|
||||
from pipenv.patched.notpip._internal.utils.misc import protect_pip_from_modification_on_windows
|
||||
|
||||
|
||||
@@ -47,7 +48,7 @@ class UninstallCommand(Command):
|
||||
with self._build_session(options) as session:
|
||||
reqs_to_uninstall = {}
|
||||
for name in args:
|
||||
req = InstallRequirement.from_line(
|
||||
req = install_req_from_line(
|
||||
name, isolated=options.isolated_mode,
|
||||
)
|
||||
if req.name:
|
||||
|
||||
@@ -4,9 +4,9 @@ from __future__ import absolute_import
|
||||
import logging
|
||||
import os
|
||||
|
||||
from pipenv.patched.notpip._internal import cmdoptions
|
||||
from pipenv.patched.notpip._internal.basecommand import RequirementCommand
|
||||
from pipenv.patched.notpip._internal.cache import WheelCache
|
||||
from pipenv.patched.notpip._internal.cli import cmdoptions
|
||||
from pipenv.patched.notpip._internal.cli.base_command import RequirementCommand
|
||||
from pipenv.patched.notpip._internal.exceptions import CommandError, PreviousBuildDirError
|
||||
from pipenv.patched.notpip._internal.operations.prepare import RequirementPreparer
|
||||
from pipenv.patched.notpip._internal.req import RequirementSet
|
||||
|
||||
@@ -18,7 +18,9 @@ import os
|
||||
from pipenv.patched.notpip._vendor import six
|
||||
from pipenv.patched.notpip._vendor.six.moves import configparser
|
||||
|
||||
from pipenv.patched.notpip._internal.exceptions import ConfigurationError
|
||||
from pipenv.patched.notpip._internal.exceptions import (
|
||||
ConfigurationError, ConfigurationFileCouldNotBeLoaded,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.locations import (
|
||||
legacy_config_file, new_config_file, running_under_virtualenv,
|
||||
site_config_files, venv_config_file,
|
||||
@@ -289,11 +291,16 @@ class Configuration(object):
|
||||
try:
|
||||
parser.read(fname)
|
||||
except UnicodeDecodeError:
|
||||
raise ConfigurationError((
|
||||
"ERROR: "
|
||||
"Configuration file contains invalid %s characters.\n"
|
||||
"Please fix your configuration, located at %s\n"
|
||||
) % (locale.getpreferredencoding(False), fname))
|
||||
# See https://github.com/pypa/pip/issues/4963
|
||||
raise ConfigurationFileCouldNotBeLoaded(
|
||||
reason="contains invalid {} characters".format(
|
||||
locale.getpreferredencoding(False)
|
||||
),
|
||||
fname=fname,
|
||||
)
|
||||
except configparser.Error as error:
|
||||
# See https://github.com/pypa/pip/issues/4893
|
||||
raise ConfigurationFileCouldNotBeLoaded(error=error)
|
||||
return parser
|
||||
|
||||
def _load_environment_vars(self):
|
||||
|
||||
@@ -323,7 +323,7 @@ class InsecureHTTPAdapter(HTTPAdapter):
|
||||
conn.ca_certs = None
|
||||
|
||||
|
||||
class PipSession(Session):
|
||||
class PipSession(requests.Session):
|
||||
|
||||
timeout = None
|
||||
|
||||
@@ -753,7 +753,7 @@ def _copy_dist_from_dir(link_path, location):
|
||||
|
||||
# build an sdist
|
||||
setup_py = 'setup.py'
|
||||
sdist_args = [os.environ.get('PIP_PYTHON_PATH', sys.executable)]
|
||||
sdist_args = [sys.executable]
|
||||
sdist_args.append('-c')
|
||||
sdist_args.append(SETUPTOOLS_SHIM % setup_py)
|
||||
sdist_args.append('sdist')
|
||||
|
||||
@@ -247,3 +247,22 @@ class HashMismatch(HashError):
|
||||
class UnsupportedPythonVersion(InstallationError):
|
||||
"""Unsupported python version according to Requires-Python package
|
||||
metadata."""
|
||||
|
||||
|
||||
class ConfigurationFileCouldNotBeLoaded(ConfigurationError):
|
||||
"""When there are errors while loading a configuration file
|
||||
"""
|
||||
|
||||
def __init__(self, reason="could not be loaded", fname=None, error=None):
|
||||
super(ConfigurationFileCouldNotBeLoaded, self).__init__(error)
|
||||
self.reason = reason
|
||||
self.fname = fname
|
||||
self.error = error
|
||||
|
||||
def __str__(self):
|
||||
if self.fname is not None:
|
||||
message_part = " in {}.".format(self.fname)
|
||||
else:
|
||||
assert self.error is not None
|
||||
message_part = ".\n{}\n".format(self.error.message)
|
||||
return "Configuration file {}{}".format(self.reason, message_part)
|
||||
|
||||
@@ -20,24 +20,27 @@ from pipenv.patched.notpip._vendor.requests.exceptions import SSLError
|
||||
from pipenv.patched.notpip._vendor.six.moves.urllib import parse as urllib_parse
|
||||
from pipenv.patched.notpip._vendor.six.moves.urllib import request as urllib_request
|
||||
|
||||
from pipenv.patched.notpip._internal.compat import ipaddress
|
||||
from pipenv.patched.notpip._internal.download import HAS_TLS, is_url, path_to_url, url_to_path
|
||||
from pipenv.patched.notpip._internal.exceptions import (
|
||||
BestVersionAlreadyInstalled, DistributionNotFound, InvalidWheelFilename,
|
||||
UnsupportedWheel,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.models.candidate import InstallationCandidate
|
||||
from pipenv.patched.notpip._internal.models.format_control import FormatControl
|
||||
from pipenv.patched.notpip._internal.models.index import PyPI
|
||||
from pipenv.patched.notpip._internal.models.link import Link
|
||||
from pipenv.patched.notpip._internal.pep425tags import get_supported
|
||||
from pipenv.patched.notpip._internal.utils.compat import ipaddress
|
||||
from pipenv.patched.notpip._internal.utils.deprecation import deprecated
|
||||
from pipenv.patched.notpip._internal.utils.logging import indent_log
|
||||
from pipenv.patched.notpip._internal.utils.misc import (
|
||||
ARCHIVE_EXTENSIONS, SUPPORTED_EXTENSIONS, cached_property, normalize_path,
|
||||
remove_auth_from_url, splitext,
|
||||
ARCHIVE_EXTENSIONS, SUPPORTED_EXTENSIONS, normalize_path,
|
||||
remove_auth_from_url,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.utils.packaging import check_requires_python
|
||||
from pipenv.patched.notpip._internal.wheel import Wheel, wheel_ext
|
||||
|
||||
__all__ = ['FormatControl', 'fmt_ctl_handle_mutual_exclude', 'PackageFinder']
|
||||
__all__ = ['FormatControl', 'PackageFinder']
|
||||
|
||||
|
||||
SECURE_ORIGINS = [
|
||||
@@ -56,46 +59,120 @@ SECURE_ORIGINS = [
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class InstallationCandidate(object):
|
||||
def _get_content_type(url, session):
|
||||
"""Get the Content-Type of the given url, using a HEAD request"""
|
||||
scheme, netloc, path, query, fragment = urllib_parse.urlsplit(url)
|
||||
if scheme not in {'http', 'https'}:
|
||||
# FIXME: some warning or something?
|
||||
# assertion error?
|
||||
return ''
|
||||
|
||||
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
|
||||
resp = session.head(url, allow_redirects=True)
|
||||
resp.raise_for_status()
|
||||
|
||||
def __repr__(self):
|
||||
return "<InstallationCandidate({!r}, {!r}, {!r})>".format(
|
||||
self.project, self.version, self.location,
|
||||
return resp.headers.get("Content-Type", "")
|
||||
|
||||
|
||||
def _handle_get_page_fail(link, reason, url, meth=None):
|
||||
if meth is None:
|
||||
meth = logger.debug
|
||||
meth("Could not fetch URL %s: %s - skipping", link, reason)
|
||||
|
||||
|
||||
def _get_html_page(link, session=None):
|
||||
if session is None:
|
||||
raise TypeError(
|
||||
"_get_html_page() missing 1 required keyword argument: 'session'"
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self._key)
|
||||
url = link.url
|
||||
url = url.split('#', 1)[0]
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._compare(other, lambda s, o: s < o)
|
||||
# Check for VCS schemes that do not support lookup as web pages.
|
||||
from pipenv.patched.notpip._internal.vcs import VcsSupport
|
||||
for scheme in VcsSupport.schemes:
|
||||
if url.lower().startswith(scheme) and url[len(scheme)] in '+:':
|
||||
logger.debug('Cannot look at %s URL %s', scheme, link)
|
||||
return None
|
||||
|
||||
def __le__(self, other):
|
||||
return self._compare(other, lambda s, o: s <= o)
|
||||
try:
|
||||
filename = link.filename
|
||||
for bad_ext in ARCHIVE_EXTENSIONS:
|
||||
if filename.endswith(bad_ext):
|
||||
content_type = _get_content_type(url, session=session)
|
||||
if content_type.lower().startswith('text/html'):
|
||||
break
|
||||
else:
|
||||
logger.debug(
|
||||
'Skipping page %s because of Content-Type: %s',
|
||||
link,
|
||||
content_type,
|
||||
)
|
||||
return
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._compare(other, lambda s, o: s == o)
|
||||
logger.debug('Getting page %s', url)
|
||||
|
||||
def __ge__(self, other):
|
||||
return self._compare(other, lambda s, o: s >= o)
|
||||
# Tack index.html onto file:// URLs that point to directories
|
||||
(scheme, netloc, path, params, query, fragment) = \
|
||||
urllib_parse.urlparse(url)
|
||||
if (scheme == 'file' and
|
||||
os.path.isdir(urllib_request.url2pathname(path))):
|
||||
# add trailing slash if not present so urljoin doesn't trim
|
||||
# final segment
|
||||
if not url.endswith('/'):
|
||||
url += '/'
|
||||
url = urllib_parse.urljoin(url, 'index.html')
|
||||
logger.debug(' file: URL is directory, getting %s', url)
|
||||
|
||||
def __gt__(self, other):
|
||||
return self._compare(other, lambda s, o: s > o)
|
||||
resp = session.get(
|
||||
url,
|
||||
headers={
|
||||
"Accept": "text/html",
|
||||
# We don't want to blindly returned cached data for
|
||||
# /simple/, because authors generally expecting that
|
||||
# twine upload && pip install will function, but if
|
||||
# they've done a pip install in the last ~10 minutes
|
||||
# it won't. Thus by setting this to zero we will not
|
||||
# blindly use any cached data, however the benefit of
|
||||
# using max-age=0 instead of no-cache, is that we will
|
||||
# still support conditional requests, so we will still
|
||||
# minimize traffic sent in cases where the page hasn't
|
||||
# changed at all, we will just always incur the round
|
||||
# trip for the conditional GET now instead of only
|
||||
# once per 10 minutes.
|
||||
# For more information, please see pypa/pip#5670.
|
||||
"Cache-Control": "max-age=0",
|
||||
},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
|
||||
def __ne__(self, other):
|
||||
return self._compare(other, lambda s, o: s != o)
|
||||
# The check for archives above only works if the url ends with
|
||||
# something that looks like an archive. However that is not a
|
||||
# requirement of an url. Unless we issue a HEAD request on every
|
||||
# url we cannot know ahead of time for sure if something is HTML
|
||||
# or not. However we can check after we've downloaded it.
|
||||
content_type = resp.headers.get('Content-Type', 'unknown')
|
||||
if not content_type.lower().startswith("text/html"):
|
||||
logger.debug(
|
||||
'Skipping page %s because of Content-Type: %s',
|
||||
link,
|
||||
content_type,
|
||||
)
|
||||
return
|
||||
|
||||
def _compare(self, other, method):
|
||||
if not isinstance(other, InstallationCandidate):
|
||||
return NotImplemented
|
||||
|
||||
return method(self._key, other._key)
|
||||
inst = HTMLPage(resp.content, resp.url, resp.headers)
|
||||
except requests.HTTPError as exc:
|
||||
_handle_get_page_fail(link, exc, url)
|
||||
except SSLError as exc:
|
||||
reason = "There was a problem confirming the ssl certificate: "
|
||||
reason += str(exc)
|
||||
_handle_get_page_fail(link, reason, url, meth=logger.info)
|
||||
except requests.ConnectionError as exc:
|
||||
_handle_get_page_fail(link, "connection error: %s" % exc, url)
|
||||
except requests.Timeout:
|
||||
_handle_get_page_fail(link, "timed out", url)
|
||||
else:
|
||||
return inst
|
||||
|
||||
|
||||
class PackageFinder(object):
|
||||
@@ -210,15 +287,15 @@ class PackageFinder(object):
|
||||
return "\n".join(lines)
|
||||
|
||||
def add_dependency_links(self, links):
|
||||
# # FIXME: this shouldn't be global list this, it should only
|
||||
# # apply to requirements of the package that specifies the
|
||||
# # dependency_links value
|
||||
# # FIXME: also, we should track comes_from (i.e., use Link)
|
||||
# FIXME: this shouldn't be global list this, it should only
|
||||
# apply to requirements of the package that specifies the
|
||||
# dependency_links value
|
||||
# FIXME: also, we should track comes_from (i.e., use Link)
|
||||
if self.process_dependency_links:
|
||||
deprecated(
|
||||
"Dependency Links processing has been deprecated and will be "
|
||||
"removed in a future release.",
|
||||
replacement=None,
|
||||
replacement="PEP 508 URL dependencies",
|
||||
gone_in="18.2",
|
||||
issue=4187,
|
||||
)
|
||||
@@ -242,6 +319,7 @@ class PackageFinder(object):
|
||||
|
||||
return extras
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _sort_locations(locations, expand_dir=False):
|
||||
"""
|
||||
@@ -467,7 +545,7 @@ class PackageFinder(object):
|
||||
logger.debug('* %s', location)
|
||||
|
||||
canonical_name = canonicalize_name(project_name)
|
||||
formats = fmt_ctl_formats(self.format_control, canonical_name)
|
||||
formats = self.format_control.get_allowed_formats(canonical_name)
|
||||
search = Search(project_name, canonical_name, formats)
|
||||
find_links_versions = self._package_versions(
|
||||
# We trust every directly linked archive in find_links
|
||||
@@ -483,7 +561,7 @@ class PackageFinder(object):
|
||||
continue
|
||||
with indent_log():
|
||||
page_versions.extend(
|
||||
self._package_versions(page.links, search)
|
||||
self._package_versions(page.iter_links(), search)
|
||||
)
|
||||
|
||||
dependency_versions = self._package_versions(
|
||||
@@ -626,7 +704,9 @@ class PackageFinder(object):
|
||||
|
||||
try:
|
||||
page = self._get_page(location)
|
||||
except requests.HTTPError as e:
|
||||
except requests.HTTPError:
|
||||
continue
|
||||
if page is None:
|
||||
continue
|
||||
|
||||
yield page
|
||||
@@ -743,7 +823,7 @@ class PackageFinder(object):
|
||||
return InstallationCandidate(search.supplied, version, link, link.requires_python)
|
||||
|
||||
def _get_page(self, link):
|
||||
return HTMLPage.get_page(link, session=self.session)
|
||||
return _get_html_page(link, session=self.session)
|
||||
|
||||
|
||||
def egg_info_matches(
|
||||
@@ -763,7 +843,7 @@ def egg_info_matches(
|
||||
return None
|
||||
if search_name is None:
|
||||
full_match = match.group(0)
|
||||
return full_match[full_match.index('-'):]
|
||||
return full_match.split('-', 1)[-1]
|
||||
name = match.group(0).lower()
|
||||
# To match the "safe" name that pkg_resources creates:
|
||||
name = name.replace('_', '-')
|
||||
@@ -775,377 +855,71 @@ def egg_info_matches(
|
||||
return None
|
||||
|
||||
|
||||
def _determine_base_url(document, page_url):
|
||||
"""Determine the HTML document's base URL.
|
||||
|
||||
This looks for a ``<base>`` tag in the HTML document. If present, its href
|
||||
attribute denotes the base URL of anchor tags in the document. If there is
|
||||
no such tag (or if it does not have a valid href attribute), the HTML
|
||||
file's URL is used as the base URL.
|
||||
|
||||
:param document: An HTML document representation. The current
|
||||
implementation expects the result of ``html5lib.parse()``.
|
||||
:param page_url: The URL of the HTML document.
|
||||
"""
|
||||
for base in document.findall(".//base"):
|
||||
href = base.get("href")
|
||||
if href is not None:
|
||||
return href
|
||||
return page_url
|
||||
|
||||
|
||||
def _get_encoding_from_headers(headers):
|
||||
"""Determine if we have any encoding information in our headers.
|
||||
"""
|
||||
if headers and "Content-Type" in headers:
|
||||
content_type, params = cgi.parse_header(headers["Content-Type"])
|
||||
if "charset" in params:
|
||||
return params['charset']
|
||||
return None
|
||||
|
||||
|
||||
_CLEAN_LINK_RE = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I)
|
||||
|
||||
|
||||
def _clean_link(url):
|
||||
"""Makes sure a link is fully encoded. That is, if a ' ' shows up in
|
||||
the link, it will be rewritten to %20 (while not over-quoting
|
||||
% or other characters)."""
|
||||
return _CLEAN_LINK_RE.sub(lambda match: '%%%2x' % ord(match.group(0)), url)
|
||||
|
||||
|
||||
class HTMLPage(object):
|
||||
"""Represents one page, along with its URL"""
|
||||
|
||||
def __init__(self, content, url, headers=None):
|
||||
# Determine if we have any encoding information in our headers
|
||||
encoding = None
|
||||
if headers and "Content-Type" in headers:
|
||||
content_type, params = cgi.parse_header(headers["Content-Type"])
|
||||
|
||||
if "charset" in params:
|
||||
encoding = params['charset']
|
||||
|
||||
self.content = content
|
||||
self.parsed = html5lib.parse(
|
||||
self.content,
|
||||
transport_encoding=encoding,
|
||||
namespaceHTMLElements=False,
|
||||
)
|
||||
self.url = url
|
||||
self.headers = headers
|
||||
|
||||
def __str__(self):
|
||||
return self.url
|
||||
|
||||
@classmethod
|
||||
def get_page(cls, link, skip_archives=True, session=None):
|
||||
if session is None:
|
||||
raise TypeError(
|
||||
"get_page() missing 1 required keyword argument: 'session'"
|
||||
)
|
||||
|
||||
url = link.url
|
||||
url = url.split('#', 1)[0]
|
||||
|
||||
# Check for VCS schemes that do not support lookup as web pages.
|
||||
from pipenv.patched.notpip._internal.vcs import VcsSupport
|
||||
for scheme in VcsSupport.schemes:
|
||||
if url.lower().startswith(scheme) and url[len(scheme)] in '+:':
|
||||
logger.debug('Cannot look at %s URL %s', scheme, link)
|
||||
return None
|
||||
|
||||
try:
|
||||
if skip_archives:
|
||||
filename = link.filename
|
||||
for bad_ext in ARCHIVE_EXTENSIONS:
|
||||
if filename.endswith(bad_ext):
|
||||
content_type = cls._get_content_type(
|
||||
url, session=session,
|
||||
)
|
||||
if content_type.lower().startswith('text/html'):
|
||||
break
|
||||
else:
|
||||
logger.debug(
|
||||
'Skipping page %s because of Content-Type: %s',
|
||||
link,
|
||||
content_type,
|
||||
)
|
||||
return
|
||||
|
||||
logger.debug('Getting page %s', url)
|
||||
|
||||
# Tack index.html onto file:// URLs that point to directories
|
||||
(scheme, netloc, path, params, query, fragment) = \
|
||||
urllib_parse.urlparse(url)
|
||||
if (scheme == 'file' and
|
||||
os.path.isdir(urllib_request.url2pathname(path))):
|
||||
# add trailing slash if not present so urljoin doesn't trim
|
||||
# final segment
|
||||
if not url.endswith('/'):
|
||||
url += '/'
|
||||
url = urllib_parse.urljoin(url, 'index.html')
|
||||
logger.debug(' file: URL is directory, getting %s', url)
|
||||
|
||||
resp = session.get(
|
||||
url,
|
||||
headers={
|
||||
"Accept": "text/html",
|
||||
"Cache-Control": "max-age=600",
|
||||
},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
|
||||
# The check for archives above only works if the url ends with
|
||||
# something that looks like an archive. However that is not a
|
||||
# requirement of an url. Unless we issue a HEAD request on every
|
||||
# url we cannot know ahead of time for sure if something is HTML
|
||||
# or not. However we can check after we've downloaded it.
|
||||
content_type = resp.headers.get('Content-Type', 'unknown')
|
||||
if not content_type.lower().startswith("text/html"):
|
||||
logger.debug(
|
||||
'Skipping page %s because of Content-Type: %s',
|
||||
link,
|
||||
content_type,
|
||||
)
|
||||
return
|
||||
|
||||
inst = cls(resp.content, resp.url, resp.headers)
|
||||
except requests.HTTPError as exc:
|
||||
cls._handle_fail(link, exc, url)
|
||||
except SSLError as exc:
|
||||
reason = "There was a problem confirming the ssl certificate: "
|
||||
reason += str(exc)
|
||||
cls._handle_fail(link, reason, url, meth=logger.info)
|
||||
except requests.ConnectionError as exc:
|
||||
cls._handle_fail(link, "connection error: %s" % exc, url)
|
||||
except requests.Timeout:
|
||||
cls._handle_fail(link, "timed out", url)
|
||||
else:
|
||||
return inst
|
||||
|
||||
@staticmethod
|
||||
def _handle_fail(link, reason, url, meth=None):
|
||||
if meth is None:
|
||||
meth = logger.debug
|
||||
|
||||
meth("Could not fetch URL %s: %s - skipping", link, reason)
|
||||
|
||||
@staticmethod
|
||||
def _get_content_type(url, session):
|
||||
"""Get the Content-Type of the given url, using a HEAD request"""
|
||||
scheme, netloc, path, query, fragment = urllib_parse.urlsplit(url)
|
||||
if scheme not in {'http', 'https'}:
|
||||
# FIXME: some warning or something?
|
||||
# assertion error?
|
||||
return ''
|
||||
|
||||
resp = session.head(url, allow_redirects=True)
|
||||
resp.raise_for_status()
|
||||
|
||||
return resp.headers.get("Content-Type", "")
|
||||
|
||||
@cached_property
|
||||
def base_url(self):
|
||||
bases = [
|
||||
x for x in self.parsed.findall(".//base")
|
||||
if x.get("href") is not None
|
||||
]
|
||||
if bases and bases[0].get("href"):
|
||||
return bases[0].get("href")
|
||||
else:
|
||||
return self.url
|
||||
|
||||
@property
|
||||
def links(self):
|
||||
def iter_links(self):
|
||||
"""Yields all links in the page"""
|
||||
for anchor in self.parsed.findall(".//a"):
|
||||
document = html5lib.parse(
|
||||
self.content,
|
||||
transport_encoding=_get_encoding_from_headers(self.headers),
|
||||
namespaceHTMLElements=False,
|
||||
)
|
||||
base_url = _determine_base_url(document, self.url)
|
||||
for anchor in document.findall(".//a"):
|
||||
if anchor.get("href"):
|
||||
href = anchor.get("href")
|
||||
url = self.clean_link(
|
||||
urllib_parse.urljoin(self.base_url, href)
|
||||
)
|
||||
url = _clean_link(urllib_parse.urljoin(base_url, href))
|
||||
pyrequire = anchor.get('data-requires-python')
|
||||
pyrequire = unescape(pyrequire) if pyrequire else None
|
||||
yield Link(url, self, requires_python=pyrequire)
|
||||
|
||||
_clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I)
|
||||
|
||||
def clean_link(self, url):
|
||||
"""Makes sure a link is fully encoded. That is, if a ' ' shows up in
|
||||
the link, it will be rewritten to %20 (while not over-quoting
|
||||
% or other characters)."""
|
||||
return self._clean_re.sub(
|
||||
lambda match: '%%%2x' % ord(match.group(0)), url)
|
||||
|
||||
|
||||
class Link(object):
|
||||
|
||||
def __init__(self, url, comes_from=None, requires_python=None):
|
||||
"""
|
||||
Object representing a parsed link from https://pypi.org/simple/*
|
||||
|
||||
url:
|
||||
url of the resource pointed to (href of the link)
|
||||
comes_from:
|
||||
instance of HTMLPage where the link was found, or string.
|
||||
requires_python:
|
||||
String containing the `Requires-Python` metadata field, specified
|
||||
in PEP 345. This may be specified by a data-requires-python
|
||||
attribute in the HTML link tag, as described in PEP 503.
|
||||
"""
|
||||
|
||||
# url can be a UNC windows share
|
||||
if url.startswith('\\\\'):
|
||||
url = path_to_url(url)
|
||||
|
||||
self.url = url
|
||||
self.comes_from = comes_from
|
||||
self.requires_python = requires_python if requires_python else None
|
||||
|
||||
def __str__(self):
|
||||
if self.requires_python:
|
||||
rp = ' (requires-python:%s)' % self.requires_python
|
||||
else:
|
||||
rp = ''
|
||||
if self.comes_from:
|
||||
return '%s (from %s)%s' % (self.url, self.comes_from, rp)
|
||||
else:
|
||||
return str(self.url)
|
||||
|
||||
def __repr__(self):
|
||||
return '<Link %s>' % self
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Link):
|
||||
return NotImplemented
|
||||
return self.url == other.url
|
||||
|
||||
def __ne__(self, other):
|
||||
if not isinstance(other, Link):
|
||||
return NotImplemented
|
||||
return self.url != other.url
|
||||
|
||||
def __lt__(self, other):
|
||||
if not isinstance(other, Link):
|
||||
return NotImplemented
|
||||
return self.url < other.url
|
||||
|
||||
def __le__(self, other):
|
||||
if not isinstance(other, Link):
|
||||
return NotImplemented
|
||||
return self.url <= other.url
|
||||
|
||||
def __gt__(self, other):
|
||||
if not isinstance(other, Link):
|
||||
return NotImplemented
|
||||
return self.url > other.url
|
||||
|
||||
def __ge__(self, other):
|
||||
if not isinstance(other, Link):
|
||||
return NotImplemented
|
||||
return self.url >= other.url
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.url)
|
||||
|
||||
@property
|
||||
def filename(self):
|
||||
_, netloc, path, _, _ = urllib_parse.urlsplit(self.url)
|
||||
name = posixpath.basename(path.rstrip('/')) or netloc
|
||||
name = urllib_parse.unquote(name)
|
||||
assert name, ('URL %r produced no filename' % self.url)
|
||||
return name
|
||||
|
||||
@property
|
||||
def scheme(self):
|
||||
return urllib_parse.urlsplit(self.url)[0]
|
||||
|
||||
@property
|
||||
def netloc(self):
|
||||
return urllib_parse.urlsplit(self.url)[1]
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return urllib_parse.unquote(urllib_parse.urlsplit(self.url)[2])
|
||||
|
||||
def splitext(self):
|
||||
return splitext(posixpath.basename(self.path.rstrip('/')))
|
||||
|
||||
@property
|
||||
def ext(self):
|
||||
return self.splitext()[1]
|
||||
|
||||
@property
|
||||
def url_without_fragment(self):
|
||||
scheme, netloc, path, query, fragment = urllib_parse.urlsplit(self.url)
|
||||
return urllib_parse.urlunsplit((scheme, netloc, path, query, None))
|
||||
|
||||
_egg_fragment_re = re.compile(r'[#&]egg=([^&]*)')
|
||||
|
||||
@property
|
||||
def egg_fragment(self):
|
||||
match = self._egg_fragment_re.search(self.url)
|
||||
if not match:
|
||||
return None
|
||||
return match.group(1)
|
||||
|
||||
_subdirectory_fragment_re = re.compile(r'[#&]subdirectory=([^&]*)')
|
||||
|
||||
@property
|
||||
def subdirectory_fragment(self):
|
||||
match = self._subdirectory_fragment_re.search(self.url)
|
||||
if not match:
|
||||
return None
|
||||
return match.group(1)
|
||||
|
||||
_hash_re = re.compile(
|
||||
r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)'
|
||||
)
|
||||
|
||||
@property
|
||||
def hash(self):
|
||||
match = self._hash_re.search(self.url)
|
||||
if match:
|
||||
return match.group(2)
|
||||
return None
|
||||
|
||||
@property
|
||||
def hash_name(self):
|
||||
match = self._hash_re.search(self.url)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
@property
|
||||
def show_url(self):
|
||||
return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0])
|
||||
|
||||
@property
|
||||
def is_wheel(self):
|
||||
return self.ext == wheel_ext
|
||||
|
||||
@property
|
||||
def is_artifact(self):
|
||||
"""
|
||||
Determines if this points to an actual artifact (e.g. a tarball) or if
|
||||
it points to an "abstract" thing like a path or a VCS location.
|
||||
"""
|
||||
from pipenv.patched.notpip._internal.vcs import vcs
|
||||
|
||||
if self.scheme in vcs.all_schemes:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
FormatControl = namedtuple('FormatControl', 'no_binary only_binary')
|
||||
"""This object has two fields, no_binary and only_binary.
|
||||
|
||||
If a field is falsy, it isn't set. If it is {':all:'}, it should match all
|
||||
packages except those listed in the other field. Only one field can be set
|
||||
to {':all:'} at a time. The rest of the time exact package name matches
|
||||
are listed, with any given package only showing up in one field at a time.
|
||||
"""
|
||||
|
||||
|
||||
def fmt_ctl_handle_mutual_exclude(value, target, other):
|
||||
new = value.split(',')
|
||||
while ':all:' in new:
|
||||
other.clear()
|
||||
target.clear()
|
||||
target.add(':all:')
|
||||
del new[:new.index(':all:') + 1]
|
||||
if ':none:' not in new:
|
||||
# Without a none, we want to discard everything as :all: covers it
|
||||
return
|
||||
for name in new:
|
||||
if name == ':none:':
|
||||
target.clear()
|
||||
continue
|
||||
name = canonicalize_name(name)
|
||||
other.discard(name)
|
||||
target.add(name)
|
||||
|
||||
|
||||
def fmt_ctl_formats(fmt_ctl, canonical_name):
|
||||
result = {"binary", "source"}
|
||||
if canonical_name in fmt_ctl.only_binary:
|
||||
result.discard('source')
|
||||
elif canonical_name in fmt_ctl.no_binary:
|
||||
result.discard('binary')
|
||||
elif ':all:' in fmt_ctl.only_binary:
|
||||
result.discard('source')
|
||||
elif ':all:' in fmt_ctl.no_binary:
|
||||
result.discard('binary')
|
||||
return frozenset(result)
|
||||
|
||||
|
||||
def fmt_ctl_no_binary(fmt_ctl):
|
||||
fmt_ctl_handle_mutual_exclude(
|
||||
':all:', fmt_ctl.no_binary, fmt_ctl.only_binary,
|
||||
)
|
||||
yield Link(url, self.url, requires_python=pyrequire)
|
||||
|
||||
|
||||
Search = namedtuple('Search', 'supplied canonical formats')
|
||||
|
||||
@@ -10,8 +10,8 @@ import sysconfig
|
||||
from distutils import sysconfig as distutils_sysconfig
|
||||
from distutils.command.install import SCHEME_KEYS # type: ignore
|
||||
|
||||
from pipenv.patched.notpip._internal.compat import WINDOWS, expanduser
|
||||
from pipenv.patched.notpip._internal.utils import appdirs
|
||||
from pipenv.patched.notpip._internal.utils.compat import WINDOWS, expanduser
|
||||
|
||||
# Application Directories
|
||||
USER_CACHE_DIR = appdirs.user_cache_dir("pip")
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
from pipenv.patched.notpip._vendor.packaging.version import parse as parse_version
|
||||
|
||||
from pipenv.patched.notpip._internal.utils.models import KeyBasedCompareMixin
|
||||
|
||||
|
||||
class InstallationCandidate(KeyBasedCompareMixin):
|
||||
"""Represents a potential "candidate" for installation.
|
||||
"""
|
||||
|
||||
def __init__(self, project, version, location, requires_python=None):
|
||||
self.project = project
|
||||
self.version = parse_version(version)
|
||||
self.location = location
|
||||
self.requires_python = requires_python
|
||||
|
||||
super(InstallationCandidate, self).__init__(
|
||||
key=(self.project, self.version, self.location),
|
||||
defining_class=InstallationCandidate
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "<InstallationCandidate({!r}, {!r}, {!r})>".format(
|
||||
self.project, self.version, self.location,
|
||||
)
|
||||
@@ -0,0 +1,62 @@
|
||||
from pipenv.patched.notpip._vendor.packaging.utils import canonicalize_name
|
||||
|
||||
|
||||
class FormatControl(object):
|
||||
"""A helper class for controlling formats from which packages are installed.
|
||||
If a field is falsy, it isn't set. If it is {':all:'}, it should match all
|
||||
packages except those listed in the other field. Only one field can be set
|
||||
to {':all:'} at a time. The rest of the time exact package name matches
|
||||
are listed, with any given package only showing up in one field at a time.
|
||||
"""
|
||||
def __init__(self, no_binary=None, only_binary=None):
|
||||
self.no_binary = set() if no_binary is None else no_binary
|
||||
self.only_binary = set() if only_binary is None else only_binary
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __repr__(self):
|
||||
return "{}({}, {})".format(
|
||||
self.__class__.__name__,
|
||||
self.no_binary,
|
||||
self.only_binary
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def handle_mutual_excludes(value, target, other):
|
||||
new = value.split(',')
|
||||
while ':all:' in new:
|
||||
other.clear()
|
||||
target.clear()
|
||||
target.add(':all:')
|
||||
del new[:new.index(':all:') + 1]
|
||||
# Without a none, we want to discard everything as :all: covers it
|
||||
if ':none:' not in new:
|
||||
return
|
||||
for name in new:
|
||||
if name == ':none:':
|
||||
target.clear()
|
||||
continue
|
||||
name = canonicalize_name(name)
|
||||
other.discard(name)
|
||||
target.add(name)
|
||||
|
||||
def get_allowed_formats(self, canonical_name):
|
||||
result = {"binary", "source"}
|
||||
if canonical_name in self.only_binary:
|
||||
result.discard('source')
|
||||
elif canonical_name in self.no_binary:
|
||||
result.discard('binary')
|
||||
elif ':all:' in self.only_binary:
|
||||
result.discard('source')
|
||||
elif ':all:' in self.no_binary:
|
||||
result.discard('binary')
|
||||
return frozenset(result)
|
||||
|
||||
def disallow_binaries(self):
|
||||
self.handle_mutual_excludes(
|
||||
':all:', self.no_binary, self.only_binary,
|
||||
)
|
||||
@@ -1,15 +1,29 @@
|
||||
from pipenv.patched.notpip._vendor.six.moves.urllib import parse as urllib_parse
|
||||
|
||||
|
||||
class Index(object):
|
||||
def __init__(self, url):
|
||||
class PackageIndex(object):
|
||||
"""Represents a Package Index and provides easier access to endpoints
|
||||
"""
|
||||
|
||||
def __init__(self, url, file_storage_domain):
|
||||
super(PackageIndex, self).__init__()
|
||||
self.url = url
|
||||
self.netloc = urllib_parse.urlsplit(url).netloc
|
||||
self.simple_url = self.url_to_path('simple')
|
||||
self.pypi_url = self.url_to_path('pypi')
|
||||
self.simple_url = self._url_for_path('simple')
|
||||
self.pypi_url = self._url_for_path('pypi')
|
||||
|
||||
def url_to_path(self, path):
|
||||
# This is part of a temporary hack used to block installs of PyPI
|
||||
# packages which depend on external urls only necessary until PyPI can
|
||||
# block such packages themselves
|
||||
self.file_storage_domain = file_storage_domain
|
||||
|
||||
def _url_for_path(self, path):
|
||||
return urllib_parse.urljoin(self.url, path)
|
||||
|
||||
|
||||
PyPI = Index('https://pypi.org/')
|
||||
PyPI = PackageIndex(
|
||||
'https://pypi.org/', file_storage_domain='files.pythonhosted.org'
|
||||
)
|
||||
TestPyPI = PackageIndex(
|
||||
'https://test.pypi.org/', file_storage_domain='test-files.pythonhosted.org'
|
||||
)
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
import posixpath
|
||||
import re
|
||||
|
||||
from pipenv.patched.notpip._vendor.six.moves.urllib import parse as urllib_parse
|
||||
|
||||
from pipenv.patched.notpip._internal.download import path_to_url
|
||||
from pipenv.patched.notpip._internal.utils.misc import splitext
|
||||
from pipenv.patched.notpip._internal.utils.models import KeyBasedCompareMixin
|
||||
from pipenv.patched.notpip._internal.wheel import wheel_ext
|
||||
|
||||
|
||||
class Link(KeyBasedCompareMixin):
|
||||
"""Represents a parsed link from a Package Index's simple URL
|
||||
"""
|
||||
|
||||
def __init__(self, url, comes_from=None, requires_python=None):
|
||||
"""
|
||||
url:
|
||||
url of the resource pointed to (href of the link)
|
||||
comes_from:
|
||||
instance of HTMLPage where the link was found, or string.
|
||||
requires_python:
|
||||
String containing the `Requires-Python` metadata field, specified
|
||||
in PEP 345. This may be specified by a data-requires-python
|
||||
attribute in the HTML link tag, as described in PEP 503.
|
||||
"""
|
||||
|
||||
# url can be a UNC windows share
|
||||
if url.startswith('\\\\'):
|
||||
url = path_to_url(url)
|
||||
|
||||
self.url = url
|
||||
self.comes_from = comes_from
|
||||
self.requires_python = requires_python if requires_python else None
|
||||
|
||||
super(Link, self).__init__(
|
||||
key=(self.url),
|
||||
defining_class=Link
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
if self.requires_python:
|
||||
rp = ' (requires-python:%s)' % self.requires_python
|
||||
else:
|
||||
rp = ''
|
||||
if self.comes_from:
|
||||
return '%s (from %s)%s' % (self.url, self.comes_from, rp)
|
||||
else:
|
||||
return str(self.url)
|
||||
|
||||
def __repr__(self):
|
||||
return '<Link %s>' % self
|
||||
|
||||
@property
|
||||
def filename(self):
|
||||
_, netloc, path, _, _ = urllib_parse.urlsplit(self.url)
|
||||
name = posixpath.basename(path.rstrip('/')) or netloc
|
||||
name = urllib_parse.unquote(name)
|
||||
assert name, ('URL %r produced no filename' % self.url)
|
||||
return name
|
||||
|
||||
@property
|
||||
def scheme(self):
|
||||
return urllib_parse.urlsplit(self.url)[0]
|
||||
|
||||
@property
|
||||
def netloc(self):
|
||||
return urllib_parse.urlsplit(self.url)[1]
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return urllib_parse.unquote(urllib_parse.urlsplit(self.url)[2])
|
||||
|
||||
def splitext(self):
|
||||
return splitext(posixpath.basename(self.path.rstrip('/')))
|
||||
|
||||
@property
|
||||
def ext(self):
|
||||
return self.splitext()[1]
|
||||
|
||||
@property
|
||||
def url_without_fragment(self):
|
||||
scheme, netloc, path, query, fragment = urllib_parse.urlsplit(self.url)
|
||||
return urllib_parse.urlunsplit((scheme, netloc, path, query, None))
|
||||
|
||||
_egg_fragment_re = re.compile(r'[#&]egg=([^&]*)')
|
||||
|
||||
@property
|
||||
def egg_fragment(self):
|
||||
match = self._egg_fragment_re.search(self.url)
|
||||
if not match:
|
||||
return None
|
||||
return match.group(1)
|
||||
|
||||
_subdirectory_fragment_re = re.compile(r'[#&]subdirectory=([^&]*)')
|
||||
|
||||
@property
|
||||
def subdirectory_fragment(self):
|
||||
match = self._subdirectory_fragment_re.search(self.url)
|
||||
if not match:
|
||||
return None
|
||||
return match.group(1)
|
||||
|
||||
_hash_re = re.compile(
|
||||
r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)'
|
||||
)
|
||||
|
||||
@property
|
||||
def hash(self):
|
||||
match = self._hash_re.search(self.url)
|
||||
if match:
|
||||
return match.group(2)
|
||||
return None
|
||||
|
||||
@property
|
||||
def hash_name(self):
|
||||
match = self._hash_re.search(self.url)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
@property
|
||||
def show_url(self):
|
||||
return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0])
|
||||
|
||||
@property
|
||||
def is_wheel(self):
|
||||
return self.ext == wheel_ext
|
||||
|
||||
@property
|
||||
def is_artifact(self):
|
||||
"""
|
||||
Determines if this points to an actual artifact (e.g. a tarball) or if
|
||||
it points to an "abstract" thing like a path or a VCS location.
|
||||
"""
|
||||
from pipenv.patched.notpip._internal.vcs import vcs
|
||||
|
||||
if self.scheme in vcs.all_schemes:
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -10,11 +10,13 @@ from pipenv.patched.notpip._vendor.packaging.utils import canonicalize_name
|
||||
from pipenv.patched.notpip._vendor.pkg_resources import RequirementParseError
|
||||
|
||||
from pipenv.patched.notpip._internal.exceptions import InstallationError
|
||||
from pipenv.patched.notpip._internal.req import InstallRequirement
|
||||
from pipenv.patched.notpip._internal.req.constructors import (
|
||||
install_req_from_editable, install_req_from_line,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.req.req_file import COMMENT_RE
|
||||
from pipenv.patched.notpip._internal.utils.deprecation import deprecated
|
||||
from pipenv.patched.notpip._internal.utils.misc import (
|
||||
dist_is_editable, get_installed_distributions,
|
||||
dist_is_editable, get_installed_distributions, make_vcs_requirement_url,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -99,13 +101,13 @@ def freeze(
|
||||
line = line[2:].strip()
|
||||
else:
|
||||
line = line[len('--editable'):].strip().lstrip('=')
|
||||
line_req = InstallRequirement.from_editable(
|
||||
line_req = install_req_from_editable(
|
||||
line,
|
||||
isolated=isolated,
|
||||
wheel_cache=wheel_cache,
|
||||
)
|
||||
else:
|
||||
line_req = InstallRequirement.from_line(
|
||||
line_req = install_req_from_line(
|
||||
COMMENT_RE.sub('', line).strip(),
|
||||
isolated=isolated,
|
||||
wheel_cache=wheel_cache,
|
||||
@@ -166,7 +168,13 @@ class FrozenRequirement(object):
|
||||
_date_re = re.compile(r'-(20\d\d\d\d\d\d)$')
|
||||
|
||||
@classmethod
|
||||
def from_dist(cls, dist, dependency_links):
|
||||
def _init_args_from_dist(cls, dist, dependency_links):
|
||||
"""
|
||||
Compute and return arguments (req, editable, comments) to pass to
|
||||
FrozenRequirement.__init__().
|
||||
|
||||
This method is for use in FrozenRequirement.from_dist().
|
||||
"""
|
||||
location = os.path.normcase(os.path.abspath(dist.location))
|
||||
comments = []
|
||||
from pipenv.patched.notpip._internal.vcs import vcs, get_src_requirement
|
||||
@@ -231,12 +239,15 @@ class FrozenRequirement(object):
|
||||
else:
|
||||
rev = '{%s}' % date_match.group(1)
|
||||
editable = True
|
||||
req = '%s@%s#egg=%s' % (
|
||||
svn_location,
|
||||
rev,
|
||||
cls.egg_name(dist)
|
||||
)
|
||||
return cls(dist.project_name, req, editable, comments)
|
||||
egg_name = cls.egg_name(dist)
|
||||
req = make_vcs_requirement_url(svn_location, rev, egg_name)
|
||||
|
||||
return (req, editable, comments)
|
||||
|
||||
@classmethod
|
||||
def from_dist(cls, dist, dependency_links):
|
||||
args = cls._init_args_from_dist(dist, dependency_links)
|
||||
return cls(dist.project_name, *args)
|
||||
|
||||
@staticmethod
|
||||
def egg_name(dist):
|
||||
|
||||
@@ -7,7 +7,6 @@ import os
|
||||
from pipenv.patched.notpip._vendor import pkg_resources, requests
|
||||
|
||||
from pipenv.patched.notpip._internal.build_env import BuildEnvironment
|
||||
from pipenv.patched.notpip._internal.compat import expanduser
|
||||
from pipenv.patched.notpip._internal.download import (
|
||||
is_dir_url, is_file_url, is_vcs_url, unpack_url, url_to_path,
|
||||
)
|
||||
@@ -15,6 +14,7 @@ from pipenv.patched.notpip._internal.exceptions import (
|
||||
DirectoryUrlHashUnsupported, HashUnpinned, InstallationError,
|
||||
PreviousBuildDirError, VcsHashUnsupported,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.utils.compat import expanduser
|
||||
from pipenv.patched.notpip._internal.utils.hashes import MissingHashes
|
||||
from pipenv.patched.notpip._internal.utils.logging import indent_log
|
||||
from pipenv.patched.notpip._internal.utils.misc import display_path, normalize_path, rmtree
|
||||
@@ -65,7 +65,7 @@ class DistAbstraction(object):
|
||||
"""Return a setuptools Dist object."""
|
||||
raise NotImplementedError(self.dist)
|
||||
|
||||
def prep_for_dist(self, finder):
|
||||
def prep_for_dist(self, finder, build_isolation):
|
||||
"""Ensure that we can get a Dist for this requirement."""
|
||||
raise NotImplementedError(self.dist)
|
||||
|
||||
@@ -93,36 +93,36 @@ class IsSDist(DistAbstraction):
|
||||
return dist
|
||||
|
||||
def prep_for_dist(self, finder, build_isolation):
|
||||
# Before calling "setup.py egg_info", we need to set-up the build
|
||||
# environment.
|
||||
build_requirements = self.req.get_pep_518_info()
|
||||
should_isolate = build_isolation and build_requirements is not None
|
||||
# Prepare for building. We need to:
|
||||
# 1. Load pyproject.toml (if it exists)
|
||||
# 2. Set up the build environment
|
||||
|
||||
self.req.load_pyproject_toml()
|
||||
should_isolate = self.req.use_pep517 and build_isolation
|
||||
|
||||
if should_isolate:
|
||||
# Haven't implemented PEP 517 yet, so spew a warning about it if
|
||||
# build-requirements don't include setuptools and wheel.
|
||||
missing_requirements = {'setuptools', 'wheel'} - {
|
||||
pkg_resources.Requirement(r).key for r in build_requirements
|
||||
}
|
||||
if missing_requirements:
|
||||
# Isolate in a BuildEnvironment and install the build-time
|
||||
# requirements.
|
||||
self.req.build_env = BuildEnvironment()
|
||||
self.req.build_env.install_requirements(
|
||||
finder, self.req.pyproject_requires,
|
||||
"Installing build dependencies"
|
||||
)
|
||||
missing = []
|
||||
if self.req.requirements_to_check:
|
||||
check = self.req.requirements_to_check
|
||||
missing = self.req.build_env.missing_requirements(check)
|
||||
if missing:
|
||||
logger.warning(
|
||||
"Missing build requirements in pyproject.toml for %s.",
|
||||
self.req,
|
||||
)
|
||||
logger.warning(
|
||||
"This version of pip does not implement PEP 517 so it "
|
||||
"cannot build a wheel without %s.",
|
||||
" and ".join(map(repr, sorted(missing_requirements)))
|
||||
"The project does not specify a build backend, and pip "
|
||||
"cannot fall back to setuptools without %s.",
|
||||
" and ".join(map(repr, sorted(missing)))
|
||||
)
|
||||
|
||||
# Isolate in a BuildEnvironment and install the build-time
|
||||
# requirements.
|
||||
self.req.build_env = BuildEnvironment()
|
||||
self.req.build_env.install_requirements(
|
||||
finder, build_requirements,
|
||||
"Installing build dependencies"
|
||||
)
|
||||
|
||||
try:
|
||||
self.req.run_egg_info()
|
||||
except (OSError, TypeError):
|
||||
@@ -136,7 +136,7 @@ class Installed(DistAbstraction):
|
||||
def dist(self, finder):
|
||||
return self.req.satisfied_by
|
||||
|
||||
def prep_for_dist(self, finder):
|
||||
def prep_for_dist(self, finder, build_isolation):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,12 @@ import sysconfig
|
||||
import warnings
|
||||
from collections import OrderedDict
|
||||
|
||||
import pipenv.patched.notpip._internal.utils.glibc
|
||||
try:
|
||||
import pipenv.patched.notpip._internal.utils.glibc
|
||||
except ImportError:
|
||||
import pipenv.patched.notpip.utils.glibc
|
||||
|
||||
from pipenv.patched.notpip._internal.utils.compat import get_extension_suffixes
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -252,10 +257,9 @@ def get_supported(versions=None, noarch=False, platform=None,
|
||||
abis[0:0] = [abi]
|
||||
|
||||
abi3s = set()
|
||||
import imp
|
||||
for suffix in imp.get_suffixes():
|
||||
if suffix[0].startswith('.abi'):
|
||||
abi3s.add(suffix[0].split('.', 2)[1])
|
||||
for suffix in get_extension_suffixes():
|
||||
if suffix.startswith('.abi'):
|
||||
abi3s.add(suffix.split('.', 2)[1])
|
||||
|
||||
abis.extend(sorted(list(abi3s)))
|
||||
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import io
|
||||
import os
|
||||
|
||||
from pipenv.patched.notpip._vendor import pytoml, six
|
||||
|
||||
from pipenv.patched.notpip._internal.exceptions import InstallationError
|
||||
|
||||
|
||||
def _is_list_of_str(obj):
|
||||
return (
|
||||
isinstance(obj, list) and
|
||||
all(isinstance(item, six.string_types) for item in obj)
|
||||
)
|
||||
|
||||
|
||||
def load_pyproject_toml(use_pep517, pyproject_toml, setup_py, req_name):
|
||||
"""Load the pyproject.toml file.
|
||||
|
||||
Parameters:
|
||||
use_pep517 - Has the user requested PEP 517 processing? None
|
||||
means the user hasn't explicitly specified.
|
||||
pyproject_toml - Location of the project's pyproject.toml file
|
||||
setup_py - Location of the project's setup.py file
|
||||
req_name - The name of the requirement we're processing (for
|
||||
error reporting)
|
||||
|
||||
Returns:
|
||||
None if we should use the legacy code path, otherwise a tuple
|
||||
(
|
||||
requirements from pyproject.toml,
|
||||
name of PEP 517 backend,
|
||||
requirements we should check are installed after setting
|
||||
up the build environment
|
||||
)
|
||||
"""
|
||||
has_pyproject = os.path.isfile(pyproject_toml)
|
||||
has_setup = os.path.isfile(setup_py)
|
||||
|
||||
if has_pyproject:
|
||||
with io.open(pyproject_toml, encoding="utf-8") as f:
|
||||
pp_toml = pytoml.load(f)
|
||||
build_system = pp_toml.get("build-system")
|
||||
else:
|
||||
build_system = None
|
||||
|
||||
# The following cases must use PEP 517
|
||||
# We check for use_pep517 equalling False because that
|
||||
# means the user explicitly requested --no-use-pep517
|
||||
if has_pyproject and not has_setup:
|
||||
if use_pep517 is False:
|
||||
raise InstallationError(
|
||||
"Disabling PEP 517 processing is invalid: "
|
||||
"project does not have a setup.py"
|
||||
)
|
||||
use_pep517 = True
|
||||
elif build_system and "build-backend" in build_system:
|
||||
if use_pep517 is False:
|
||||
raise InstallationError(
|
||||
"Disabling PEP 517 processing is invalid: "
|
||||
"project specifies a build backend of {} "
|
||||
"in pyproject.toml".format(
|
||||
build_system["build-backend"]
|
||||
)
|
||||
)
|
||||
use_pep517 = True
|
||||
|
||||
# If we haven't worked out whether to use PEP 517 yet,
|
||||
# and the user hasn't explicitly stated a preference,
|
||||
# we do so if the project has a pyproject.toml file.
|
||||
elif use_pep517 is None:
|
||||
use_pep517 = has_pyproject
|
||||
|
||||
# At this point, we know whether we're going to use PEP 517.
|
||||
assert use_pep517 is not None
|
||||
|
||||
# If we're using the legacy code path, there is nothing further
|
||||
# for us to do here.
|
||||
if not use_pep517:
|
||||
return None
|
||||
|
||||
if build_system is None:
|
||||
# Either the user has a pyproject.toml with no build-system
|
||||
# section, or the user has no pyproject.toml, but has opted in
|
||||
# explicitly via --use-pep517.
|
||||
# In the absence of any explicit backend specification, we
|
||||
# assume the setuptools backend, and require wheel and a version
|
||||
# of setuptools that supports that backend.
|
||||
build_system = {
|
||||
"requires": ["setuptools>=38.2.5", "wheel"],
|
||||
"build-backend": "setuptools.build_meta",
|
||||
}
|
||||
|
||||
# If we're using PEP 517, we have build system information (either
|
||||
# from pyproject.toml, or defaulted by the code above).
|
||||
# Note that at this point, we do not know if the user has actually
|
||||
# specified a backend, though.
|
||||
assert build_system is not None
|
||||
|
||||
# Ensure that the build-system section in pyproject.toml conforms
|
||||
# to PEP 518.
|
||||
error_template = (
|
||||
"{package} has a pyproject.toml file that does not comply "
|
||||
"with PEP 518: {reason}"
|
||||
)
|
||||
|
||||
# Specifying the build-system table but not the requires key is invalid
|
||||
if "requires" not in build_system:
|
||||
raise InstallationError(
|
||||
error_template.format(package=req_name, reason=(
|
||||
"it has a 'build-system' table but not "
|
||||
"'build-system.requires' which is mandatory in the table"
|
||||
))
|
||||
)
|
||||
|
||||
# Error out if requires is not a list of strings
|
||||
requires = build_system["requires"]
|
||||
if not _is_list_of_str(requires):
|
||||
raise InstallationError(error_template.format(
|
||||
package=req_name,
|
||||
reason="'build-system.requires' is not a list of strings.",
|
||||
))
|
||||
|
||||
backend = build_system.get("build-backend")
|
||||
check = []
|
||||
if backend is None:
|
||||
# If the user didn't specify a backend, we assume they want to use
|
||||
# the setuptools backend. But we can't be sure they have included
|
||||
# a version of setuptools which supplies the backend, or wheel
|
||||
# (which is neede by the backend) in their requirements. So we
|
||||
# make a note to check that those requirements are present once
|
||||
# we have set up the environment.
|
||||
# TODO: Review this - it's quite a lot of work to check for a very
|
||||
# specific case. The problem is, that case is potentially quite
|
||||
# common - projects that adopted PEP 518 early for the ability to
|
||||
# specify requirements to execute setup.py, but never considered
|
||||
# needing to mention the build tools themselves. The original PEP
|
||||
# 518 code had a similar check (but implemented in a different
|
||||
# way).
|
||||
backend = "setuptools.build_meta"
|
||||
check = ["setuptools>=38.2.5", "wheel"]
|
||||
|
||||
return (requires, backend, check)
|
||||
@@ -0,0 +1,298 @@
|
||||
"""Backing implementation for InstallRequirement's various constructors
|
||||
|
||||
The idea here is that these formed a major chunk of InstallRequirement's size
|
||||
so, moving them and support code dedicated to them outside of that class
|
||||
helps creates for better understandability for the rest of the code.
|
||||
|
||||
These are meant to be used elsewhere within pip to create instances of
|
||||
InstallRequirement.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import traceback
|
||||
|
||||
from pipenv.patched.notpip._vendor.packaging.markers import Marker
|
||||
from pipenv.patched.notpip._vendor.packaging.requirements import InvalidRequirement, Requirement
|
||||
from pipenv.patched.notpip._vendor.packaging.specifiers import Specifier
|
||||
from pipenv.patched.notpip._vendor.pkg_resources import RequirementParseError, parse_requirements
|
||||
|
||||
from pipenv.patched.notpip._internal.download import (
|
||||
is_archive_file, is_url, path_to_url, url_to_path,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.exceptions import InstallationError
|
||||
from pipenv.patched.notpip._internal.models.index import PyPI, TestPyPI
|
||||
from pipenv.patched.notpip._internal.models.link import Link
|
||||
from pipenv.patched.notpip._internal.req.req_install import InstallRequirement
|
||||
from pipenv.patched.notpip._internal.utils.misc import is_installable_dir
|
||||
from pipenv.patched.notpip._internal.vcs import vcs
|
||||
from pipenv.patched.notpip._internal.wheel import Wheel
|
||||
|
||||
__all__ = [
|
||||
"install_req_from_editable", "install_req_from_line",
|
||||
"parse_editable"
|
||||
]
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
operators = Specifier._operators.keys()
|
||||
|
||||
|
||||
def _strip_extras(path):
|
||||
m = re.match(r'^(.+)(\[[^\]]+\])$', path)
|
||||
extras = None
|
||||
if m:
|
||||
path_no_extras = m.group(1)
|
||||
extras = m.group(2)
|
||||
else:
|
||||
path_no_extras = path
|
||||
|
||||
return path_no_extras, extras
|
||||
|
||||
|
||||
def parse_editable(editable_req):
|
||||
"""Parses an editable requirement into:
|
||||
- a requirement name
|
||||
- an URL
|
||||
- extras
|
||||
- editable options
|
||||
Accepted requirements:
|
||||
svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
|
||||
.[some_extra]
|
||||
"""
|
||||
|
||||
url = editable_req
|
||||
|
||||
# If a file path is specified with extras, strip off the extras.
|
||||
url_no_extras, extras = _strip_extras(url)
|
||||
|
||||
if os.path.isdir(url_no_extras):
|
||||
if not os.path.exists(os.path.join(url_no_extras, 'setup.py')):
|
||||
raise InstallationError(
|
||||
"Directory %r is not installable. File 'setup.py' not found." %
|
||||
url_no_extras
|
||||
)
|
||||
# Treating it as code that has already been checked out
|
||||
url_no_extras = path_to_url(url_no_extras)
|
||||
|
||||
if url_no_extras.lower().startswith('file:'):
|
||||
package_name = Link(url_no_extras).egg_fragment
|
||||
if extras:
|
||||
return (
|
||||
package_name,
|
||||
url_no_extras,
|
||||
Requirement("placeholder" + extras.lower()).extras,
|
||||
)
|
||||
else:
|
||||
return package_name, url_no_extras, None
|
||||
|
||||
for version_control in vcs:
|
||||
if url.lower().startswith('%s:' % version_control):
|
||||
url = '%s+%s' % (version_control, url)
|
||||
break
|
||||
|
||||
if '+' not in url:
|
||||
raise InstallationError(
|
||||
'%s should either be a path to a local project or a VCS url '
|
||||
'beginning with svn+, git+, hg+, or bzr+' %
|
||||
editable_req
|
||||
)
|
||||
|
||||
vc_type = url.split('+', 1)[0].lower()
|
||||
|
||||
if not vcs.get_backend(vc_type):
|
||||
error_message = 'For --editable=%s only ' % editable_req + \
|
||||
', '.join([backend.name + '+URL' for backend in vcs.backends]) + \
|
||||
' is currently supported'
|
||||
raise InstallationError(error_message)
|
||||
|
||||
package_name = Link(url).egg_fragment
|
||||
if not package_name:
|
||||
raise InstallationError(
|
||||
"Could not detect requirement name for '%s', please specify one "
|
||||
"with #egg=your_package_name" % editable_req
|
||||
)
|
||||
return package_name, url, None
|
||||
|
||||
|
||||
def deduce_helpful_msg(req):
|
||||
"""Returns helpful msg in case requirements file does not exist,
|
||||
or cannot be parsed.
|
||||
|
||||
:params req: Requirements file path
|
||||
"""
|
||||
msg = ""
|
||||
if os.path.exists(req):
|
||||
msg = " It does exist."
|
||||
# Try to parse and check if it is a requirements file.
|
||||
try:
|
||||
with open(req, 'r') as fp:
|
||||
# parse first line only
|
||||
next(parse_requirements(fp.read()))
|
||||
msg += " The argument you provided " + \
|
||||
"(%s) appears to be a" % (req) + \
|
||||
" requirements file. If that is the" + \
|
||||
" case, use the '-r' flag to install" + \
|
||||
" the packages specified within it."
|
||||
except RequirementParseError:
|
||||
logger.debug("Cannot parse '%s' as requirements \
|
||||
file" % (req), exc_info=1)
|
||||
else:
|
||||
msg += " File '%s' does not exist." % (req)
|
||||
return msg
|
||||
|
||||
|
||||
# ---- The actual constructors follow ----
|
||||
|
||||
|
||||
def install_req_from_editable(
|
||||
editable_req, comes_from=None, isolated=False, options=None,
|
||||
wheel_cache=None, constraint=False
|
||||
):
|
||||
name, url, extras_override = parse_editable(editable_req)
|
||||
if url.startswith('file:'):
|
||||
source_dir = url_to_path(url)
|
||||
else:
|
||||
source_dir = None
|
||||
|
||||
if name is not None:
|
||||
try:
|
||||
req = Requirement(name)
|
||||
except InvalidRequirement:
|
||||
raise InstallationError("Invalid requirement: '%s'" % name)
|
||||
else:
|
||||
req = None
|
||||
return InstallRequirement(
|
||||
req, comes_from, source_dir=source_dir,
|
||||
editable=True,
|
||||
link=Link(url),
|
||||
constraint=constraint,
|
||||
isolated=isolated,
|
||||
options=options if options else {},
|
||||
wheel_cache=wheel_cache,
|
||||
extras=extras_override or (),
|
||||
)
|
||||
|
||||
|
||||
def install_req_from_line(
|
||||
name, comes_from=None, isolated=False, options=None, wheel_cache=None,
|
||||
constraint=False
|
||||
):
|
||||
"""Creates an InstallRequirement from a name, which might be a
|
||||
requirement, directory containing 'setup.py', filename, or URL.
|
||||
"""
|
||||
if is_url(name):
|
||||
marker_sep = '; '
|
||||
else:
|
||||
marker_sep = ';'
|
||||
if marker_sep in name:
|
||||
name, markers = name.split(marker_sep, 1)
|
||||
markers = markers.strip()
|
||||
if not markers:
|
||||
markers = None
|
||||
else:
|
||||
markers = Marker(markers)
|
||||
else:
|
||||
markers = None
|
||||
name = name.strip()
|
||||
req = None
|
||||
path = os.path.normpath(os.path.abspath(name))
|
||||
link = None
|
||||
extras = None
|
||||
|
||||
if is_url(name):
|
||||
link = Link(name)
|
||||
else:
|
||||
p, extras = _strip_extras(path)
|
||||
looks_like_dir = os.path.isdir(p) and (
|
||||
os.path.sep in name or
|
||||
(os.path.altsep is not None and os.path.altsep in name) or
|
||||
name.startswith('.')
|
||||
)
|
||||
if looks_like_dir:
|
||||
if not is_installable_dir(p):
|
||||
raise InstallationError(
|
||||
"Directory %r is not installable. Neither 'setup.py' "
|
||||
"nor 'pyproject.toml' found." % name
|
||||
)
|
||||
link = Link(path_to_url(p))
|
||||
elif is_archive_file(p):
|
||||
if not os.path.isfile(p):
|
||||
logger.warning(
|
||||
'Requirement %r looks like a filename, but the '
|
||||
'file does not exist',
|
||||
name
|
||||
)
|
||||
link = Link(path_to_url(p))
|
||||
|
||||
# it's a local file, dir, or url
|
||||
if link:
|
||||
# Handle relative file URLs
|
||||
if link.scheme == 'file' and re.search(r'\.\./', link.url):
|
||||
link = Link(
|
||||
path_to_url(os.path.normpath(os.path.abspath(link.path))))
|
||||
# wheel file
|
||||
if link.is_wheel:
|
||||
wheel = Wheel(link.filename) # can raise InvalidWheelFilename
|
||||
req = "%s==%s" % (wheel.name, wheel.version)
|
||||
else:
|
||||
# set the req to the egg fragment. when it's not there, this
|
||||
# will become an 'unnamed' requirement
|
||||
req = link.egg_fragment
|
||||
|
||||
# a requirement specifier
|
||||
else:
|
||||
req = name
|
||||
|
||||
if extras:
|
||||
extras = Requirement("placeholder" + extras.lower()).extras
|
||||
else:
|
||||
extras = ()
|
||||
if req is not None:
|
||||
try:
|
||||
req = Requirement(req)
|
||||
except InvalidRequirement:
|
||||
if os.path.sep in req:
|
||||
add_msg = "It looks like a path."
|
||||
add_msg += deduce_helpful_msg(req)
|
||||
elif '=' in req and not any(op in req for op in operators):
|
||||
add_msg = "= is not a valid operator. Did you mean == ?"
|
||||
else:
|
||||
add_msg = traceback.format_exc()
|
||||
raise InstallationError(
|
||||
"Invalid requirement: '%s'\n%s" % (req, add_msg)
|
||||
)
|
||||
|
||||
return InstallRequirement(
|
||||
req, comes_from, link=link, markers=markers,
|
||||
isolated=isolated,
|
||||
options=options if options else {},
|
||||
wheel_cache=wheel_cache,
|
||||
constraint=constraint,
|
||||
extras=extras,
|
||||
)
|
||||
|
||||
|
||||
def install_req_from_req(
|
||||
req, comes_from=None, isolated=False, wheel_cache=None
|
||||
):
|
||||
try:
|
||||
req = Requirement(req)
|
||||
except InvalidRequirement:
|
||||
raise InstallationError("Invalid requirement: '%s'" % req)
|
||||
|
||||
domains_not_allowed = [
|
||||
PyPI.file_storage_domain,
|
||||
TestPyPI.file_storage_domain,
|
||||
]
|
||||
if req.url and comes_from.link.netloc in domains_not_allowed:
|
||||
# Explicitly disallow pypi packages that depend on external urls
|
||||
raise InstallationError(
|
||||
"Packages installed from PyPI cannot depend on packages "
|
||||
"which are not also hosted on PyPI.\n"
|
||||
"%s depends on %s " % (comes_from.name, req)
|
||||
)
|
||||
|
||||
return InstallRequirement(
|
||||
req, comes_from, isolated=isolated, wheel_cache=wheel_cache
|
||||
)
|
||||
@@ -13,10 +13,12 @@ import sys
|
||||
from pipenv.patched.notpip._vendor.six.moves import filterfalse
|
||||
from pipenv.patched.notpip._vendor.six.moves.urllib import parse as urllib_parse
|
||||
|
||||
from pipenv.patched.notpip._internal import cmdoptions
|
||||
from pipenv.patched.notpip._internal.cli import cmdoptions
|
||||
from pipenv.patched.notpip._internal.download import get_file_content
|
||||
from pipenv.patched.notpip._internal.exceptions import RequirementsFileParseError
|
||||
from pipenv.patched.notpip._internal.req.req_install import InstallRequirement
|
||||
from pipenv.patched.notpip._internal.req.constructors import (
|
||||
install_req_from_editable, install_req_from_line,
|
||||
)
|
||||
|
||||
__all__ = ['parse_requirements']
|
||||
|
||||
@@ -151,7 +153,7 @@ def process_line(line, filename, line_number, finder=None, comes_from=None,
|
||||
for dest in SUPPORTED_OPTIONS_REQ_DEST:
|
||||
if dest in opts.__dict__ and opts.__dict__[dest]:
|
||||
req_options[dest] = opts.__dict__[dest]
|
||||
yield InstallRequirement.from_line(
|
||||
yield install_req_from_line(
|
||||
args_str, line_comes_from, constraint=constraint,
|
||||
isolated=isolated, options=req_options, wheel_cache=wheel_cache
|
||||
)
|
||||
@@ -159,7 +161,7 @@ def process_line(line, filename, line_number, finder=None, comes_from=None,
|
||||
# yield an editable requirement
|
||||
elif opts.editables:
|
||||
isolated = options.isolated_mode if options else False
|
||||
yield InstallRequirement.from_editable(
|
||||
yield install_req_from_editable(
|
||||
opts.editables[0], comes_from=line_comes_from,
|
||||
constraint=constraint, isolated=isolated, wheel_cache=wheel_cache
|
||||
)
|
||||
|
||||
@@ -1,66 +1,46 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import io
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import sysconfig
|
||||
import traceback
|
||||
import zipfile
|
||||
from distutils.util import change_root
|
||||
from email.parser import FeedParser # type: ignore
|
||||
|
||||
from pipenv.patched.notpip._vendor import pkg_resources, pytoml, six
|
||||
from pipenv.patched.notpip._vendor.packaging import specifiers
|
||||
from pipenv.patched.notpip._vendor.packaging.markers import Marker
|
||||
from pipenv.patched.notpip._vendor.packaging.requirements import InvalidRequirement, Requirement
|
||||
from pipenv.patched.notpip._vendor import pkg_resources, six
|
||||
from pipenv.patched.notpip._vendor.packaging.requirements import Requirement
|
||||
from pipenv.patched.notpip._vendor.packaging.utils import canonicalize_name
|
||||
from pipenv.patched.notpip._vendor.packaging.version import Version
|
||||
from pipenv.patched.notpip._vendor.packaging.version import parse as parse_version
|
||||
from pipenv.patched.notpip._vendor.pkg_resources import RequirementParseError, parse_requirements
|
||||
from pipenv.patched.notpip._vendor.pep517.wrappers import Pep517HookCaller
|
||||
|
||||
from pipenv.patched.notpip._internal import wheel
|
||||
from pipenv.patched.notpip._internal.build_env import NoOpBuildEnvironment
|
||||
from pipenv.patched.notpip._internal.compat import native_str
|
||||
from pipenv.patched.notpip._internal.download import (
|
||||
is_archive_file, is_url, path_to_url, url_to_path,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.exceptions import InstallationError
|
||||
from pipenv.patched.notpip._internal.locations import (
|
||||
PIP_DELETE_MARKER_FILENAME, running_under_virtualenv,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.models.link import Link
|
||||
from pipenv.patched.notpip._internal.pyproject import load_pyproject_toml
|
||||
from pipenv.patched.notpip._internal.req.req_uninstall import UninstallPathSet
|
||||
from pipenv.patched.notpip._internal.utils.compat import native_str
|
||||
from pipenv.patched.notpip._internal.utils.hashes import Hashes
|
||||
from pipenv.patched.notpip._internal.utils.logging import indent_log
|
||||
from pipenv.patched.notpip._internal.utils.misc import (
|
||||
_make_build_dir, ask_path_exists, backup_dir, call_subprocess,
|
||||
display_path, dist_in_site_packages, dist_in_usersite, ensure_dir,
|
||||
get_installed_version, is_installable_dir, read_text_file, rmtree,
|
||||
get_installed_version, rmtree,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.utils.packaging import get_metadata
|
||||
from pipenv.patched.notpip._internal.utils.setuptools_build import SETUPTOOLS_SHIM
|
||||
from pipenv.patched.notpip._internal.utils.temp_dir import TempDirectory
|
||||
from pipenv.patched.notpip._internal.utils.ui import open_spinner
|
||||
from pipenv.patched.notpip._internal.vcs import vcs
|
||||
from pipenv.patched.notpip._internal.wheel import Wheel, move_wheel_files
|
||||
from pipenv.patched.notpip._internal.wheel import move_wheel_files
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
operators = specifiers.Specifier._operators.keys()
|
||||
|
||||
|
||||
def _strip_extras(path):
|
||||
m = re.match(r'^(.+)(\[[^\]]+\])$', path)
|
||||
extras = None
|
||||
if m:
|
||||
path_no_extras = m.group(1)
|
||||
extras = m.group(2)
|
||||
else:
|
||||
path_no_extras = path
|
||||
|
||||
return path_no_extras, extras
|
||||
|
||||
|
||||
class InstallRequirement(object):
|
||||
"""
|
||||
@@ -87,7 +67,6 @@ class InstallRequirement(object):
|
||||
if link is not None:
|
||||
self.link = self.original_link = link
|
||||
else:
|
||||
from pipenv.patched.notpip._internal.index import Link
|
||||
self.link = self.original_link = req and req.url and Link(req.url)
|
||||
|
||||
if extras:
|
||||
@@ -128,155 +107,32 @@ class InstallRequirement(object):
|
||||
self.isolated = isolated
|
||||
self.build_env = NoOpBuildEnvironment()
|
||||
|
||||
# Constructors
|
||||
# TODO: Move these out of this class into custom methods.
|
||||
@classmethod
|
||||
def from_editable(cls, editable_req, comes_from=None, isolated=False,
|
||||
options=None, wheel_cache=None, constraint=False):
|
||||
from pipenv.patched.notpip._internal.index import Link
|
||||
# The static build requirements (from pyproject.toml)
|
||||
self.pyproject_requires = None
|
||||
|
||||
name, url, extras_override = parse_editable(editable_req)
|
||||
if url.startswith('file:'):
|
||||
source_dir = url_to_path(url)
|
||||
else:
|
||||
source_dir = None
|
||||
# Build requirements that we will check are available
|
||||
# TODO: We don't do this for --no-build-isolation. Should we?
|
||||
self.requirements_to_check = []
|
||||
|
||||
if name is not None:
|
||||
try:
|
||||
req = Requirement(name)
|
||||
except InvalidRequirement:
|
||||
raise InstallationError("Invalid requirement: '%s'" % name)
|
||||
else:
|
||||
req = None
|
||||
return cls(
|
||||
req, comes_from, source_dir=source_dir,
|
||||
editable=True,
|
||||
link=Link(url),
|
||||
constraint=constraint,
|
||||
isolated=isolated,
|
||||
options=options if options else {},
|
||||
wheel_cache=wheel_cache,
|
||||
extras=extras_override or (),
|
||||
)
|
||||
# The PEP 517 backend we should use to build the project
|
||||
self.pep517_backend = None
|
||||
|
||||
@classmethod
|
||||
def from_req(cls, req, comes_from=None, isolated=False, wheel_cache=None):
|
||||
try:
|
||||
req = Requirement(req)
|
||||
except InvalidRequirement:
|
||||
raise InstallationError("Invalid requirement: '%s'" % req)
|
||||
if req.url:
|
||||
raise InstallationError(
|
||||
"Direct url requirement (like %s) are not allowed for "
|
||||
"dependencies" % req
|
||||
)
|
||||
return cls(req, comes_from, isolated=isolated, wheel_cache=wheel_cache)
|
||||
|
||||
@classmethod
|
||||
def from_line(
|
||||
cls, name, comes_from=None, isolated=False, options=None,
|
||||
wheel_cache=None, constraint=False):
|
||||
"""Creates an InstallRequirement from a name, which might be a
|
||||
requirement, directory containing 'setup.py', filename, or URL.
|
||||
"""
|
||||
from pipenv.patched.notpip._internal.index import Link
|
||||
|
||||
if is_url(name):
|
||||
marker_sep = '; '
|
||||
else:
|
||||
marker_sep = ';'
|
||||
if marker_sep in name:
|
||||
name, markers = name.split(marker_sep, 1)
|
||||
markers = markers.strip()
|
||||
if not markers:
|
||||
markers = None
|
||||
else:
|
||||
markers = Marker(markers)
|
||||
else:
|
||||
markers = None
|
||||
name = name.strip()
|
||||
req = None
|
||||
path = os.path.normpath(os.path.abspath(name))
|
||||
link = None
|
||||
extras = None
|
||||
|
||||
if is_url(name):
|
||||
link = Link(name)
|
||||
else:
|
||||
p, extras = _strip_extras(path)
|
||||
looks_like_dir = os.path.isdir(p) and (
|
||||
os.path.sep in name or
|
||||
(os.path.altsep is not None and os.path.altsep in name) or
|
||||
name.startswith('.')
|
||||
)
|
||||
if looks_like_dir:
|
||||
if not is_installable_dir(p):
|
||||
raise InstallationError(
|
||||
"Directory %r is not installable. File 'setup.py' "
|
||||
"not found." % name
|
||||
)
|
||||
link = Link(path_to_url(p))
|
||||
elif is_archive_file(p):
|
||||
if not os.path.isfile(p):
|
||||
logger.warning(
|
||||
'Requirement %r looks like a filename, but the '
|
||||
'file does not exist',
|
||||
name
|
||||
)
|
||||
link = Link(path_to_url(p))
|
||||
|
||||
# it's a local file, dir, or url
|
||||
if link:
|
||||
# Handle relative file URLs
|
||||
if link.scheme == 'file' and re.search(r'\.\./', link.url):
|
||||
link = Link(
|
||||
path_to_url(os.path.normpath(os.path.abspath(link.path))))
|
||||
# wheel file
|
||||
if link.is_wheel:
|
||||
wheel = Wheel(link.filename) # can raise InvalidWheelFilename
|
||||
req = "%s==%s" % (wheel.name, wheel.version)
|
||||
else:
|
||||
# set the req to the egg fragment. when it's not there, this
|
||||
# will become an 'unnamed' requirement
|
||||
req = link.egg_fragment
|
||||
|
||||
# a requirement specifier
|
||||
else:
|
||||
req = name
|
||||
|
||||
if extras:
|
||||
extras = Requirement("placeholder" + extras.lower()).extras
|
||||
else:
|
||||
extras = ()
|
||||
if req is not None:
|
||||
try:
|
||||
req = Requirement(req)
|
||||
except InvalidRequirement:
|
||||
if os.path.sep in req:
|
||||
add_msg = "It looks like a path."
|
||||
add_msg += deduce_helpful_msg(req)
|
||||
elif '=' in req and not any(op in req for op in operators):
|
||||
add_msg = "= is not a valid operator. Did you mean == ?"
|
||||
else:
|
||||
add_msg = traceback.format_exc()
|
||||
raise InstallationError(
|
||||
"Invalid requirement: '%s'\n%s" % (req, add_msg))
|
||||
return cls(
|
||||
req, comes_from, link=link, markers=markers,
|
||||
isolated=isolated,
|
||||
options=options if options else {},
|
||||
wheel_cache=wheel_cache,
|
||||
constraint=constraint,
|
||||
extras=extras,
|
||||
)
|
||||
# Are we using PEP 517 for this requirement?
|
||||
# After pyproject.toml has been loaded, the only valid values are True
|
||||
# and False. Before loading, None is valid (meaning "use the default").
|
||||
# Setting an explicit value before loading pyproject.toml is supported,
|
||||
# but after loading this flag should be treated as read only.
|
||||
self.use_pep517 = None
|
||||
|
||||
def __str__(self):
|
||||
if self.req:
|
||||
s = str(self.req)
|
||||
if self.link:
|
||||
s += ' from %s' % self.link.url
|
||||
elif self.link:
|
||||
s = self.link.url
|
||||
else:
|
||||
s = self.link.url if self.link else None
|
||||
s = '<InstallRequirement>'
|
||||
if self.satisfied_by is not None:
|
||||
s += ' in %s' % display_path(self.satisfied_by.location)
|
||||
if self.comes_from:
|
||||
@@ -429,7 +285,7 @@ class InstallRequirement(object):
|
||||
package is not available until we run egg_info, so the build_location
|
||||
will return a temporary directory and store the _ideal_build_dir.
|
||||
|
||||
This is only called by self.egg_info_path to fix the temporary build
|
||||
This is only called by self.run_egg_info to fix the temporary build
|
||||
directory.
|
||||
"""
|
||||
if self.source_dir is not None:
|
||||
@@ -557,48 +413,29 @@ class InstallRequirement(object):
|
||||
|
||||
return pp_toml
|
||||
|
||||
def get_pep_518_info(self):
|
||||
"""Get PEP 518 build-time requirements.
|
||||
def load_pyproject_toml(self):
|
||||
"""Load the pyproject.toml file.
|
||||
|
||||
Returns the list of the packages required to build the project,
|
||||
specified as per PEP 518 within the package. If `pyproject.toml` is not
|
||||
present, returns None to signify not using the same.
|
||||
After calling this routine, all of the attributes related to PEP 517
|
||||
processing for this requirement have been set. In particular, the
|
||||
use_pep517 attribute can be used to determine whether we should
|
||||
follow the PEP 517 or legacy (setup.py) code path.
|
||||
"""
|
||||
# If pyproject.toml does not exist, don't do anything.
|
||||
if not os.path.isfile(self.pyproject_toml):
|
||||
return None
|
||||
|
||||
error_template = (
|
||||
"{package} has a pyproject.toml file that does not comply "
|
||||
"with PEP 518: {reason}"
|
||||
pep517_data = load_pyproject_toml(
|
||||
self.use_pep517,
|
||||
self.pyproject_toml,
|
||||
self.setup_py,
|
||||
str(self)
|
||||
)
|
||||
|
||||
with io.open(self.pyproject_toml, encoding="utf-8") as f:
|
||||
pp_toml = pytoml.load(f)
|
||||
|
||||
# If there is no build-system table, just use setuptools and wheel.
|
||||
if "build-system" not in pp_toml:
|
||||
return ["setuptools", "wheel"]
|
||||
|
||||
# Specifying the build-system table but not the requires key is invalid
|
||||
build_system = pp_toml["build-system"]
|
||||
if "requires" not in build_system:
|
||||
raise InstallationError(
|
||||
error_template.format(package=self, reason=(
|
||||
"it has a 'build-system' table but not "
|
||||
"'build-system.requires' which is mandatory in the table"
|
||||
))
|
||||
)
|
||||
|
||||
# Error out if it's not a list of strings
|
||||
requires = build_system["requires"]
|
||||
if not _is_list_of_str(requires):
|
||||
raise InstallationError(error_template.format(
|
||||
package=self,
|
||||
reason="'build-system.requires' is not a list of strings.",
|
||||
))
|
||||
|
||||
return requires
|
||||
if pep517_data is None:
|
||||
self.use_pep517 = False
|
||||
else:
|
||||
self.use_pep517 = True
|
||||
requires, backend, check = pep517_data
|
||||
self.requirements_to_check = check
|
||||
self.pyproject_requires = requires
|
||||
self.pep517_backend = Pep517HookCaller(self.setup_py_dir, backend)
|
||||
|
||||
def run_egg_info(self):
|
||||
assert self.source_dir
|
||||
@@ -615,7 +452,8 @@ class InstallRequirement(object):
|
||||
|
||||
with indent_log():
|
||||
script = SETUPTOOLS_SHIM % self.setup_py
|
||||
base_cmd = [os.environ.get('PIP_PYTHON_PATH', sys.executable), '-c', script]
|
||||
sys_executable = os.environ.get('PIP_PYTHON_PATH', sys.executable)
|
||||
base_cmd = [sys_executable, '-c', script]
|
||||
if self.isolated:
|
||||
base_cmd += ["--no-user-cfg"]
|
||||
egg_info_cmd = base_cmd + ['egg_info']
|
||||
@@ -636,20 +474,20 @@ class InstallRequirement(object):
|
||||
command_desc='python setup.py egg_info')
|
||||
|
||||
if not self.req:
|
||||
if isinstance(parse_version(self.pkg_info()["Version"]), Version):
|
||||
if isinstance(parse_version(self.metadata["Version"]), Version):
|
||||
op = "=="
|
||||
else:
|
||||
op = "==="
|
||||
self.req = Requirement(
|
||||
"".join([
|
||||
self.pkg_info()["Name"],
|
||||
self.metadata["Name"],
|
||||
op,
|
||||
self.pkg_info()["Version"],
|
||||
self.metadata["Version"],
|
||||
])
|
||||
)
|
||||
self._correct_build_location()
|
||||
else:
|
||||
metadata_name = canonicalize_name(self.pkg_info()["Name"])
|
||||
metadata_name = canonicalize_name(self.metadata["Name"])
|
||||
if canonicalize_name(self.req.name) != metadata_name:
|
||||
logger.warning(
|
||||
'Running setup.py (path:%s) egg_info for package %s '
|
||||
@@ -659,19 +497,8 @@ class InstallRequirement(object):
|
||||
)
|
||||
self.req = Requirement(metadata_name)
|
||||
|
||||
def egg_info_data(self, filename):
|
||||
if self.satisfied_by is not None:
|
||||
if not self.satisfied_by.has_metadata(filename):
|
||||
return None
|
||||
return self.satisfied_by.get_metadata(filename)
|
||||
assert self.source_dir
|
||||
filename = self.egg_info_path(filename)
|
||||
if not os.path.exists(filename):
|
||||
return None
|
||||
data = read_text_file(filename)
|
||||
return data
|
||||
|
||||
def egg_info_path(self, filename):
|
||||
@property
|
||||
def egg_info_path(self):
|
||||
if self._egg_info_path is None:
|
||||
if self.editable:
|
||||
base = self.source_dir
|
||||
@@ -709,8 +536,7 @@ class InstallRequirement(object):
|
||||
|
||||
if not filenames:
|
||||
raise InstallationError(
|
||||
"Files/directories (from %s) not found in %s"
|
||||
% (filename, base)
|
||||
"Files/directories not found in %s" % base
|
||||
)
|
||||
# if we have more than one match, we pick the toplevel one. This
|
||||
# can easily be the case if there is a dist folder which contains
|
||||
@@ -721,24 +547,18 @@ class InstallRequirement(object):
|
||||
(os.path.altsep and x.count(os.path.altsep) or 0)
|
||||
)
|
||||
self._egg_info_path = os.path.join(base, filenames[0])
|
||||
return os.path.join(self._egg_info_path, filename)
|
||||
return self._egg_info_path
|
||||
|
||||
def pkg_info(self):
|
||||
p = FeedParser()
|
||||
data = self.egg_info_data('PKG-INFO')
|
||||
if not data:
|
||||
logger.warning(
|
||||
'No PKG-INFO file found in %s',
|
||||
display_path(self.egg_info_path('PKG-INFO')),
|
||||
)
|
||||
p.feed(data or '')
|
||||
return p.close()
|
||||
@property
|
||||
def metadata(self):
|
||||
if not hasattr(self, '_metadata'):
|
||||
self._metadata = get_metadata(self.get_dist())
|
||||
|
||||
_requirements_section_re = re.compile(r'\[(.*?)\]')
|
||||
return self._metadata
|
||||
|
||||
def get_dist(self):
|
||||
"""Return a pkg_resources.Distribution built from self.egg_info_path"""
|
||||
egg_info = self.egg_info_path('').rstrip(os.path.sep)
|
||||
egg_info = self.egg_info_path.rstrip(os.path.sep)
|
||||
base_dir = os.path.dirname(egg_info)
|
||||
metadata = pkg_resources.PathMetadata(base_dir, egg_info)
|
||||
dist_name = os.path.splitext(os.path.basename(egg_info))[0]
|
||||
@@ -750,7 +570,7 @@ class InstallRequirement(object):
|
||||
|
||||
def assert_source_matches_version(self):
|
||||
assert self.source_dir
|
||||
version = self.pkg_info()['version']
|
||||
version = self.metadata['version']
|
||||
if self.req.specifier and version not in self.req.specifier:
|
||||
logger.warning(
|
||||
'Requested %s, but installing version %s',
|
||||
@@ -794,10 +614,11 @@ class InstallRequirement(object):
|
||||
|
||||
with indent_log():
|
||||
# FIXME: should we do --install-headers here too?
|
||||
sys_executable = os.environ.get('PIP_PYTHON_PATH', sys.executable)
|
||||
with self.build_env:
|
||||
call_subprocess(
|
||||
[
|
||||
os.environ.get('PIP_PYTHON_PATH', sys.executable),
|
||||
sys_executable,
|
||||
'-c',
|
||||
SETUPTOOLS_SHIM % self.setup_py
|
||||
] +
|
||||
@@ -877,7 +698,7 @@ class InstallRequirement(object):
|
||||
def archive(self, build_dir):
|
||||
assert self.source_dir
|
||||
create_archive = True
|
||||
archive_name = '%s-%s.zip' % (self.name, self.pkg_info()["version"])
|
||||
archive_name = '%s-%s.zip' % (self.name, self.metadata["version"])
|
||||
archive_path = os.path.join(build_dir, archive_name)
|
||||
if os.path.exists(archive_path):
|
||||
response = ask_path_exists(
|
||||
@@ -1015,7 +836,8 @@ class InstallRequirement(object):
|
||||
|
||||
def get_install_args(self, global_options, record_filename, root, prefix,
|
||||
pycompile):
|
||||
install_args = [os.environ.get('PIP_PYTHON_PATH', sys.executable), "-u"]
|
||||
sys_executable = os.environ.get('PIP_PYTHON_PATH', sys.executable)
|
||||
install_args = [sys_executable, "-u"]
|
||||
install_args.append('-c')
|
||||
install_args.append(SETUPTOOLS_SHIM % self.setup_py)
|
||||
install_args += list(global_options) + \
|
||||
@@ -1039,104 +861,3 @@ class InstallRequirement(object):
|
||||
py_ver_str, self.name)]
|
||||
|
||||
return install_args
|
||||
|
||||
|
||||
def parse_editable(editable_req):
|
||||
"""Parses an editable requirement into:
|
||||
- a requirement name
|
||||
- an URL
|
||||
- extras
|
||||
- editable options
|
||||
Accepted requirements:
|
||||
svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
|
||||
.[some_extra]
|
||||
"""
|
||||
|
||||
from pipenv.patched.notpip._internal.index import Link
|
||||
|
||||
url = editable_req
|
||||
|
||||
# If a file path is specified with extras, strip off the extras.
|
||||
url_no_extras, extras = _strip_extras(url)
|
||||
|
||||
if os.path.isdir(url_no_extras):
|
||||
if not os.path.exists(os.path.join(url_no_extras, 'setup.py')):
|
||||
raise InstallationError(
|
||||
"Directory %r is not installable. File 'setup.py' not found." %
|
||||
url_no_extras
|
||||
)
|
||||
# Treating it as code that has already been checked out
|
||||
url_no_extras = path_to_url(url_no_extras)
|
||||
|
||||
if url_no_extras.lower().startswith('file:'):
|
||||
package_name = Link(url_no_extras).egg_fragment
|
||||
if extras:
|
||||
return (
|
||||
package_name,
|
||||
url_no_extras,
|
||||
Requirement("placeholder" + extras.lower()).extras,
|
||||
)
|
||||
else:
|
||||
return package_name, url_no_extras, None
|
||||
|
||||
for version_control in vcs:
|
||||
if url.lower().startswith('%s:' % version_control):
|
||||
url = '%s+%s' % (version_control, url)
|
||||
break
|
||||
|
||||
if '+' not in url:
|
||||
raise InstallationError(
|
||||
'%s should either be a path to a local project or a VCS url '
|
||||
'beginning with svn+, git+, hg+, or bzr+' %
|
||||
editable_req
|
||||
)
|
||||
|
||||
vc_type = url.split('+', 1)[0].lower()
|
||||
|
||||
if not vcs.get_backend(vc_type):
|
||||
error_message = 'For --editable=%s only ' % editable_req + \
|
||||
', '.join([backend.name + '+URL' for backend in vcs.backends]) + \
|
||||
' is currently supported'
|
||||
raise InstallationError(error_message)
|
||||
|
||||
package_name = Link(url).egg_fragment
|
||||
if not package_name:
|
||||
raise InstallationError(
|
||||
"Could not detect requirement name for '%s', please specify one "
|
||||
"with #egg=your_package_name" % editable_req
|
||||
)
|
||||
return package_name, url, None
|
||||
|
||||
|
||||
def deduce_helpful_msg(req):
|
||||
"""Returns helpful msg in case requirements file does not exist,
|
||||
or cannot be parsed.
|
||||
|
||||
:params req: Requirements file path
|
||||
"""
|
||||
msg = ""
|
||||
if os.path.exists(req):
|
||||
msg = " It does exist."
|
||||
# Try to parse and check if it is a requirements file.
|
||||
try:
|
||||
with open(req, 'r') as fp:
|
||||
# parse first line only
|
||||
next(parse_requirements(fp.read()))
|
||||
msg += " The argument you provided " + \
|
||||
"(%s) appears to be a" % (req) + \
|
||||
" requirements file. If that is the" + \
|
||||
" case, use the '-r' flag to install" + \
|
||||
" the packages specified within it."
|
||||
except RequirementParseError:
|
||||
logger.debug("Cannot parse '%s' as requirements \
|
||||
file" % (req), exc_info=1)
|
||||
else:
|
||||
msg += " File '%s' does not exist." % (req)
|
||||
return msg
|
||||
|
||||
|
||||
def _is_list_of_str(obj):
|
||||
return (
|
||||
isinstance(obj, list) and
|
||||
all(isinstance(item, six.string_types) for item in obj)
|
||||
)
|
||||
|
||||
@@ -12,19 +12,22 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class RequirementSet(object):
|
||||
|
||||
def __init__(self, require_hashes=False, ignore_compatibility=True):
|
||||
def __init__(self, require_hashes=False, check_supported_wheels=True, ignore_compatibility=True):
|
||||
"""Create a RequirementSet.
|
||||
"""
|
||||
|
||||
self.requirements = OrderedDict()
|
||||
self.require_hashes = require_hashes
|
||||
self.check_supported_wheels = check_supported_wheels
|
||||
if ignore_compatibility:
|
||||
self.check_supported_wheels = False
|
||||
self.ignore_compatibility = True if (check_supported_wheels is False or ignore_compatibility is True) else False
|
||||
|
||||
# Mapping of alias: real_name
|
||||
self.requirement_aliases = {}
|
||||
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()
|
||||
@@ -56,17 +59,22 @@ class RequirementSet(object):
|
||||
requirement is applicable and has just been added.
|
||||
"""
|
||||
name = install_req.name
|
||||
|
||||
# If the markers do not match, ignore this requirement.
|
||||
if not install_req.match_markers(extras_requested):
|
||||
logger.info("Ignoring %s: markers '%s' don't match your "
|
||||
"environment", install_req.name,
|
||||
install_req.markers)
|
||||
logger.info(
|
||||
"Ignoring %s: markers '%s' don't match your environment",
|
||||
name, install_req.markers,
|
||||
)
|
||||
return [], None
|
||||
|
||||
# This check has to come after we filter requirements with the
|
||||
# environment markers.
|
||||
# If the wheel is not supported, raise an error.
|
||||
# Should check this after filtering out based on environment markers to
|
||||
# allow specifying different wheels based on the environment/OS, in a
|
||||
# single requirements file.
|
||||
if install_req.link and install_req.link.is_wheel:
|
||||
wheel = Wheel(install_req.link.filename)
|
||||
if not wheel.supported() and not self.ignore_compatibility:
|
||||
if self.check_supported_wheels and not wheel.supported():
|
||||
raise InstallationError(
|
||||
"%s is not a supported wheel on this platform." %
|
||||
wheel.filename
|
||||
@@ -78,59 +86,73 @@ class RequirementSet(object):
|
||||
"a non direct req should have a parent"
|
||||
)
|
||||
|
||||
# Unnamed requirements are scanned again and the requirement won't be
|
||||
# added as a dependency until after scanning.
|
||||
if not name:
|
||||
# url or path requirement w/o an egg fragment
|
||||
self.unnamed_requirements.append(install_req)
|
||||
return [install_req], None
|
||||
else:
|
||||
try:
|
||||
existing_req = self.get_requirement(name)
|
||||
except KeyError:
|
||||
existing_req = None
|
||||
if (parent_req_name is None and existing_req and not
|
||||
existing_req.constraint and
|
||||
existing_req.extras == install_req.extras and not
|
||||
existing_req.req.specifier == install_req.req.specifier):
|
||||
raise InstallationError(
|
||||
'Double requirement given: %s (already in %s, name=%r)'
|
||||
% (install_req, existing_req, name))
|
||||
if not existing_req:
|
||||
# Add requirement
|
||||
self.requirements[name] = install_req
|
||||
# FIXME: what about other normalizations? E.g., _ vs. -?
|
||||
if name.lower() != name:
|
||||
self.requirement_aliases[name.lower()] = name
|
||||
result = [install_req]
|
||||
else:
|
||||
# Assume there's no need to scan, and that we've already
|
||||
# encountered this for scanning.
|
||||
result = []
|
||||
if not install_req.constraint and existing_req.constraint:
|
||||
if (install_req.link and not (existing_req.link and
|
||||
install_req.link.path == existing_req.link.path)):
|
||||
self.reqs_to_cleanup.append(install_req)
|
||||
raise InstallationError(
|
||||
"Could not satisfy constraints for '%s': "
|
||||
"installation from path or url cannot be "
|
||||
"constrained to a version" % name,
|
||||
)
|
||||
# If we're now installing a constraint, mark the existing
|
||||
# object for real installation.
|
||||
existing_req.constraint = False
|
||||
existing_req.extras = tuple(
|
||||
sorted(set(existing_req.extras).union(
|
||||
set(install_req.extras))))
|
||||
logger.debug("Setting %s extras to: %s",
|
||||
existing_req, existing_req.extras)
|
||||
# And now we need to scan this.
|
||||
result = [existing_req]
|
||||
# Canonicalise to the already-added object for the backref
|
||||
# check below.
|
||||
install_req = existing_req
|
||||
|
||||
# We return install_req here to allow for the caller to add it to
|
||||
# the dependency information for the parent package.
|
||||
return result, install_req
|
||||
try:
|
||||
existing_req = self.get_requirement(name)
|
||||
except KeyError:
|
||||
existing_req = None
|
||||
|
||||
has_conflicting_requirement = (
|
||||
parent_req_name is None and
|
||||
existing_req and
|
||||
not existing_req.constraint and
|
||||
existing_req.extras == install_req.extras and
|
||||
existing_req.req.specifier != install_req.req.specifier
|
||||
)
|
||||
if has_conflicting_requirement:
|
||||
raise InstallationError(
|
||||
"Double requirement given: %s (already in %s, name=%r)"
|
||||
% (install_req, existing_req, name)
|
||||
)
|
||||
|
||||
# When no existing requirement exists, add the requirement as a
|
||||
# dependency and it will be scanned again after.
|
||||
if not existing_req:
|
||||
self.requirements[name] = install_req
|
||||
# FIXME: what about other normalizations? E.g., _ vs. -?
|
||||
if name.lower() != name:
|
||||
self.requirement_aliases[name.lower()] = name
|
||||
# We'd want to rescan this requirements later
|
||||
return [install_req], install_req
|
||||
|
||||
# Assume there's no need to scan, and that we've already
|
||||
# encountered this for scanning.
|
||||
if install_req.constraint or not existing_req.constraint:
|
||||
return [], existing_req
|
||||
|
||||
does_not_satisfy_constraint = (
|
||||
install_req.link and
|
||||
not (
|
||||
existing_req.link and
|
||||
install_req.link.path == existing_req.link.path
|
||||
)
|
||||
)
|
||||
if does_not_satisfy_constraint:
|
||||
self.reqs_to_cleanup.append(install_req)
|
||||
raise InstallationError(
|
||||
"Could not satisfy constraints for '%s': "
|
||||
"installation from path or url cannot be "
|
||||
"constrained to a version" % name,
|
||||
)
|
||||
# If we're now installing a constraint, mark the existing
|
||||
# object for real installation.
|
||||
existing_req.constraint = False
|
||||
existing_req.extras = tuple(sorted(
|
||||
set(existing_req.extras) | set(install_req.extras)
|
||||
))
|
||||
logger.debug(
|
||||
"Setting %s extras to: %s",
|
||||
existing_req, existing_req.extras,
|
||||
)
|
||||
# Return the existing requirement for addition to the parent and
|
||||
# scanning again.
|
||||
return [existing_req], existing_req
|
||||
|
||||
def has_requirement(self, project_name):
|
||||
name = project_name.lower()
|
||||
@@ -152,7 +174,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)
|
||||
pass
|
||||
|
||||
def cleanup_files(self):
|
||||
"""Clean up files, remove builds."""
|
||||
|
||||
@@ -9,9 +9,9 @@ import sysconfig
|
||||
|
||||
from pipenv.patched.notpip._vendor import pkg_resources
|
||||
|
||||
from pipenv.patched.notpip._internal.compat import WINDOWS, cache_from_source, uses_pycache
|
||||
from pipenv.patched.notpip._internal.exceptions import UninstallationError
|
||||
from pipenv.patched.notpip._internal.locations import bin_py, bin_user
|
||||
from pipenv.patched.notpip._internal.utils.compat import WINDOWS, cache_from_source, uses_pycache
|
||||
from pipenv.patched.notpip._internal.utils.logging import indent_log
|
||||
from pipenv.patched.notpip._internal.utils.misc import (
|
||||
FakeFile, ask, dist_in_usersite, dist_is_local, egg_link_path, is_local,
|
||||
@@ -120,6 +120,8 @@ def compress_for_output_listing(paths):
|
||||
folders.add(os.path.dirname(path))
|
||||
files.add(path)
|
||||
|
||||
_normcased_files = set(map(os.path.normcase, files))
|
||||
|
||||
folders = compact(folders)
|
||||
|
||||
# This walks the tree using os.walk to not miss extra folders
|
||||
@@ -130,8 +132,9 @@ def compress_for_output_listing(paths):
|
||||
if fname.endswith(".pyc"):
|
||||
continue
|
||||
|
||||
file_ = os.path.normcase(os.path.join(dirpath, fname))
|
||||
if os.path.isfile(file_) and file_ not in files:
|
||||
file_ = os.path.join(dirpath, fname)
|
||||
if (os.path.isfile(file_) and
|
||||
os.path.normcase(file_) not in _normcased_files):
|
||||
# We are skipping this file. Add it to the set.
|
||||
will_skip.add(file_)
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ from pipenv.patched.notpip._internal.exceptions import (
|
||||
BestVersionAlreadyInstalled, DistributionNotFound, HashError, HashErrors,
|
||||
UnsupportedPythonVersion,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.req.req_install import InstallRequirement
|
||||
from pipenv.patched.notpip._internal.req.constructors import install_req_from_req
|
||||
from pipenv.patched.notpip._internal.utils.logging import indent_log
|
||||
from pipenv.patched.notpip._internal.utils.misc import dist_in_usersite, ensure_dir
|
||||
from pipenv.patched.notpip._internal.utils.packaging import check_dist_requires_python
|
||||
@@ -249,9 +249,6 @@ 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 []
|
||||
|
||||
@@ -267,7 +264,7 @@ class Resolver(object):
|
||||
try:
|
||||
check_dist_requires_python(dist)
|
||||
except UnsupportedPythonVersion as err:
|
||||
if self.ignore_compatibility:
|
||||
if self.ignore_requires_python or self.ignore_compatibility:
|
||||
logger.warning(err.args[0])
|
||||
else:
|
||||
raise
|
||||
@@ -281,7 +278,7 @@ class Resolver(object):
|
||||
more_reqs = []
|
||||
|
||||
def add_req(subreq, extras_requested):
|
||||
sub_install_req = InstallRequirement.from_req(
|
||||
sub_install_req = install_req_from_req(
|
||||
str(subreq),
|
||||
req_to_install,
|
||||
isolated=self.isolated,
|
||||
@@ -303,10 +300,10 @@ 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):
|
||||
# 'unnamed' requirements will get added here
|
||||
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,
|
||||
@@ -338,7 +335,7 @@ class Resolver(object):
|
||||
for available in available_requested:
|
||||
if hasattr(dist, '_DistInfoDistribution__dep_map'):
|
||||
for req in dist._DistInfoDistribution__dep_map[available]:
|
||||
req = InstallRequirement.from_req(
|
||||
req = install_req_from_req(
|
||||
str(req),
|
||||
req_to_install,
|
||||
isolated=self.isolated,
|
||||
|
||||
@@ -9,7 +9,7 @@ import sys
|
||||
|
||||
from pipenv.patched.notpip._vendor.six import PY2, text_type
|
||||
|
||||
from pipenv.patched.notpip._internal.compat import WINDOWS, expanduser
|
||||
from pipenv.patched.notpip._internal.utils.compat import WINDOWS, expanduser
|
||||
|
||||
|
||||
def user_cache_dir(appname):
|
||||
|
||||
+13
@@ -25,6 +25,7 @@ except ImportError:
|
||||
__all__ = [
|
||||
"ipaddress", "uses_pycache", "console_to_str", "native_str",
|
||||
"get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", "get_terminal_size",
|
||||
"get_extension_suffixes",
|
||||
]
|
||||
|
||||
|
||||
@@ -160,6 +161,18 @@ def get_path_uid(path):
|
||||
return file_uid
|
||||
|
||||
|
||||
if sys.version_info >= (3, 4):
|
||||
from importlib.machinery import EXTENSION_SUFFIXES
|
||||
|
||||
def get_extension_suffixes():
|
||||
return EXTENSION_SUFFIXES
|
||||
else:
|
||||
from imp import get_suffixes
|
||||
|
||||
def get_extension_suffixes():
|
||||
return [suffix[0] for suffix in get_suffixes()]
|
||||
|
||||
|
||||
def expanduser(path):
|
||||
"""
|
||||
Expand ~ and ~user constructions.
|
||||
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
import os.path
|
||||
|
||||
from pipenv.patched.notpip._internal.compat import get_path_uid
|
||||
from pipenv.patched.notpip._internal.utils.compat import get_path_uid
|
||||
|
||||
|
||||
def check_path_owner(path):
|
||||
|
||||
@@ -5,7 +5,7 @@ import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
|
||||
from pipenv.patched.notpip._internal.compat import WINDOWS
|
||||
from pipenv.patched.notpip._internal.utils.compat import WINDOWS
|
||||
from pipenv.patched.notpip._internal.utils.misc import ensure_dir
|
||||
|
||||
try:
|
||||
|
||||
@@ -26,14 +26,14 @@ from pipenv.patched.notpip._vendor.six import PY2
|
||||
from pipenv.patched.notpip._vendor.six.moves import input
|
||||
from pipenv.patched.notpip._vendor.six.moves.urllib import parse as urllib_parse
|
||||
|
||||
from pipenv.patched.notpip._internal.compat import (
|
||||
WINDOWS, console_to_str, expanduser, stdlib_pkgs,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.exceptions import CommandError, InstallationError
|
||||
from pipenv.patched.notpip._internal.locations import (
|
||||
running_under_virtualenv, site_packages, user_site, virtualenv_no_global,
|
||||
write_delete_marker_file,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.utils.compat import (
|
||||
WINDOWS, console_to_str, expanduser, stdlib_pkgs,
|
||||
)
|
||||
|
||||
if PY2:
|
||||
from io import BytesIO as StringIO
|
||||
@@ -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" % os.environ.get('PIP_PYTHON_PATH', sys.executable)
|
||||
return "%s -m pip" % sys.executable
|
||||
else:
|
||||
return prog
|
||||
except (AttributeError, TypeError, IndexError):
|
||||
@@ -187,12 +187,16 @@ def format_size(bytes):
|
||||
|
||||
|
||||
def is_installable_dir(path):
|
||||
"""Return True if `path` is a directory containing a setup.py file."""
|
||||
"""Is path is a directory containing setup.py or pyproject.toml?
|
||||
"""
|
||||
if not os.path.isdir(path):
|
||||
return False
|
||||
setup_py = os.path.join(path, 'setup.py')
|
||||
if os.path.isfile(setup_py):
|
||||
return True
|
||||
pyproject_toml = os.path.join(path, 'pyproject.toml')
|
||||
if os.path.isfile(pyproject_toml):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -852,6 +856,44 @@ def enum(*sequential, **named):
|
||||
return type('Enum', (), enums)
|
||||
|
||||
|
||||
def make_vcs_requirement_url(repo_url, rev, egg_project_name, subdir=None):
|
||||
"""
|
||||
Return the URL for a VCS requirement.
|
||||
|
||||
Args:
|
||||
repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+").
|
||||
"""
|
||||
req = '{}@{}#egg={}'.format(repo_url, rev, egg_project_name)
|
||||
if subdir:
|
||||
req += '&subdirectory={}'.format(subdir)
|
||||
|
||||
return req
|
||||
|
||||
|
||||
def split_auth_from_netloc(netloc):
|
||||
"""
|
||||
Parse out and remove the auth information from a netloc.
|
||||
|
||||
Returns: (netloc, (username, password)).
|
||||
"""
|
||||
if '@' not in netloc:
|
||||
return netloc, (None, None)
|
||||
|
||||
# Split from the right because that's how urllib.parse.urlsplit()
|
||||
# behaves if more than one @ is present (which can be checked using
|
||||
# the password attribute of urlsplit()'s return value).
|
||||
auth, netloc = netloc.rsplit('@', 1)
|
||||
if ':' in auth:
|
||||
# Split from the left because that's how urllib.parse.urlsplit()
|
||||
# behaves if more than one : is present (which again can be checked
|
||||
# using the password attribute of the return value)
|
||||
user_pass = tuple(auth.split(':', 1))
|
||||
else:
|
||||
user_pass = auth, None
|
||||
|
||||
return netloc, user_pass
|
||||
|
||||
|
||||
def remove_auth_from_url(url):
|
||||
# Return a copy of url with 'username:password@' removed.
|
||||
# username/pass params are passed to subversion through flags
|
||||
@@ -859,12 +901,11 @@ def remove_auth_from_url(url):
|
||||
|
||||
# parsed url
|
||||
purl = urllib_parse.urlsplit(url)
|
||||
stripped_netloc = \
|
||||
purl.netloc.split('@')[-1]
|
||||
netloc, user_pass = split_auth_from_netloc(purl.netloc)
|
||||
|
||||
# stripped url
|
||||
url_pieces = (
|
||||
purl.scheme, stripped_netloc, purl.path, purl.query, purl.fragment
|
||||
purl.scheme, netloc, purl.path, purl.query, purl.fragment
|
||||
)
|
||||
surl = urllib_parse.urlunsplit(url_pieces)
|
||||
return surl
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Utilities for defining models
|
||||
"""
|
||||
|
||||
import operator
|
||||
|
||||
|
||||
class KeyBasedCompareMixin(object):
|
||||
"""Provides comparision capabilities that is based on a key
|
||||
"""
|
||||
|
||||
def __init__(self, key, defining_class):
|
||||
self._compare_key = key
|
||||
self._defining_class = defining_class
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self._compare_key)
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._compare(other, operator.__lt__)
|
||||
|
||||
def __le__(self, other):
|
||||
return self._compare(other, operator.__le__)
|
||||
|
||||
def __gt__(self, other):
|
||||
return self._compare(other, operator.__gt__)
|
||||
|
||||
def __ge__(self, other):
|
||||
return self._compare(other, operator.__ge__)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._compare(other, operator.__eq__)
|
||||
|
||||
def __ne__(self, other):
|
||||
return self._compare(other, operator.__ne__)
|
||||
|
||||
def _compare(self, other, method):
|
||||
if not isinstance(other, self._defining_class):
|
||||
return NotImplemented
|
||||
|
||||
return method(self._compare_key, other._compare_key)
|
||||
@@ -9,8 +9,8 @@ import sys
|
||||
from pipenv.patched.notpip._vendor import lockfile, pkg_resources
|
||||
from pipenv.patched.notpip._vendor.packaging import version as packaging_version
|
||||
|
||||
from pipenv.patched.notpip._internal.compat import WINDOWS
|
||||
from pipenv.patched.notpip._internal.index import PackageFinder
|
||||
from pipenv.patched.notpip._internal.utils.compat import WINDOWS
|
||||
from pipenv.patched.notpip._internal.utils.filesystem import check_path_owner
|
||||
from pipenv.patched.notpip._internal.utils.misc import ensure_dir, get_installed_version
|
||||
|
||||
@@ -22,16 +22,25 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class SelfCheckState(object):
|
||||
def __init__(self, cache_dir):
|
||||
self.statefile_path = os.path.join(cache_dir, "selfcheck.json")
|
||||
self.state = {}
|
||||
self.statefile_path = None
|
||||
|
||||
# Load the existing state
|
||||
try:
|
||||
with open(self.statefile_path) as statefile:
|
||||
self.state = json.load(statefile)[sys.prefix]
|
||||
except (IOError, ValueError, KeyError):
|
||||
self.state = {}
|
||||
# Try to load the existing state
|
||||
if cache_dir:
|
||||
self.statefile_path = os.path.join(cache_dir, "selfcheck.json")
|
||||
try:
|
||||
with open(self.statefile_path) as statefile:
|
||||
self.state = json.load(statefile)[sys.prefix]
|
||||
except (IOError, ValueError, KeyError):
|
||||
# Explicitly suppressing exceptions, since we don't want to
|
||||
# error out if the cache file is invalid.
|
||||
pass
|
||||
|
||||
def save(self, pypi_version, current_time):
|
||||
# If we do not have a path to cache in, don't bother saving.
|
||||
if not self.statefile_path:
|
||||
return
|
||||
|
||||
# Check to make sure that we own the directory
|
||||
if not check_path_owner(os.path.dirname(self.statefile_path)):
|
||||
return
|
||||
|
||||
@@ -8,6 +8,7 @@ from pipenv.patched.notpip._vendor import pkg_resources
|
||||
from pipenv.patched.notpip._vendor.packaging import specifiers, version
|
||||
|
||||
from pipenv.patched.notpip._internal import exceptions
|
||||
from pipenv.patched.notpip._internal.utils.misc import display_path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -35,22 +36,31 @@ def check_requires_python(requires_python):
|
||||
def get_metadata(dist):
|
||||
if (isinstance(dist, pkg_resources.DistInfoDistribution) and
|
||||
dist.has_metadata('METADATA')):
|
||||
return dist.get_metadata('METADATA')
|
||||
metadata = dist.get_metadata('METADATA')
|
||||
elif dist.has_metadata('PKG-INFO'):
|
||||
return dist.get_metadata('PKG-INFO')
|
||||
metadata = dist.get_metadata('PKG-INFO')
|
||||
else:
|
||||
logger.warning("No metadata found in %s", display_path(dist.location))
|
||||
metadata = ''
|
||||
|
||||
feed_parser = FeedParser()
|
||||
feed_parser.feed(metadata)
|
||||
return feed_parser.close()
|
||||
|
||||
|
||||
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()
|
||||
pkg_info_dict = get_metadata(dist)
|
||||
requires_python = pkg_info_dict.get('Requires-Python')
|
||||
if not absorb:
|
||||
if absorb:
|
||||
return requires_python
|
||||
try:
|
||||
if not check_requires_python(requires_python):
|
||||
return 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])),)
|
||||
)
|
||||
except specifiers.InvalidSpecifier as e:
|
||||
logger.warning(
|
||||
"Package %s has an invalid Requires-Python entry %s - %s",
|
||||
|
||||
@@ -15,7 +15,7 @@ from pipenv.patched.notpip._vendor.progress.bar import (
|
||||
from pipenv.patched.notpip._vendor.progress.helpers import HIDE_CURSOR, SHOW_CURSOR, WritelnMixin
|
||||
from pipenv.patched.notpip._vendor.progress.spinner import Spinner
|
||||
|
||||
from pipenv.patched.notpip._internal.compat import WINDOWS
|
||||
from pipenv.patched.notpip._internal.utils.compat import WINDOWS
|
||||
from pipenv.patched.notpip._internal.utils.logging import get_indentation
|
||||
from pipenv.patched.notpip._internal.utils.misc import format_size
|
||||
from pipenv.patched.notpip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
@@ -17,7 +17,7 @@ from pipenv.patched.notpip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Dict, Optional, Tuple # noqa: F401
|
||||
from pipenv.patched.notpip._internal.basecommand import Command # noqa: F401
|
||||
from pipenv.patched.notpip._internal.cli.base_command import Command # noqa: F401
|
||||
|
||||
__all__ = ['vcs', 'get_src_requirement']
|
||||
|
||||
@@ -200,12 +200,6 @@ class VersionControl(object):
|
||||
drive, tail = os.path.splitdrive(repo)
|
||||
return repo.startswith(os.path.sep) or drive
|
||||
|
||||
# See issue #1083 for why this method was introduced:
|
||||
# https://github.com/pypa/pip/issues/1083
|
||||
def translate_egg_surname(self, surname):
|
||||
# For example, Django has branches of the form "stable/1.7.x".
|
||||
return surname.replace('/', '_')
|
||||
|
||||
def export(self, location):
|
||||
"""
|
||||
Export the repository at the url to the destination location
|
||||
@@ -213,51 +207,65 @@ class VersionControl(object):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_url_rev(self, url):
|
||||
def get_netloc_and_auth(self, netloc, scheme):
|
||||
"""
|
||||
Returns the correct repository URL and revision by parsing the given
|
||||
repository URL
|
||||
Parse the repository URL's netloc, and return the new netloc to use
|
||||
along with auth information.
|
||||
|
||||
Args:
|
||||
netloc: the original repository URL netloc.
|
||||
scheme: the repository URL's scheme without the vcs prefix.
|
||||
|
||||
This is mainly for the Subversion class to override, so that auth
|
||||
information can be provided via the --username and --password options
|
||||
instead of through the URL. For other subclasses like Git without
|
||||
such an option, auth information must stay in the URL.
|
||||
|
||||
Returns: (netloc, (username, password)).
|
||||
"""
|
||||
return netloc, (None, None)
|
||||
|
||||
def get_url_rev_and_auth(self, url):
|
||||
"""
|
||||
Parse the repository URL to use, and return the URL, revision,
|
||||
and auth info to use.
|
||||
|
||||
Returns: (url, rev, (username, password)).
|
||||
"""
|
||||
error_message = (
|
||||
"Sorry, '%s' is a malformed VCS url. "
|
||||
"The format is <vcs>+<protocol>://<url>, "
|
||||
"e.g. svn+http://myrepo/svn/MyApp#egg=MyApp"
|
||||
)
|
||||
assert '+' in url, error_message % url
|
||||
url = url.split('+', 1)[1]
|
||||
scheme, netloc, path, query, frag = urllib_parse.urlsplit(url)
|
||||
if '+' not in scheme:
|
||||
raise ValueError(
|
||||
"Sorry, {!r} is a malformed VCS url. "
|
||||
"The format is <vcs>+<protocol>://<url>, "
|
||||
"e.g. svn+http://myrepo/svn/MyApp#egg=MyApp".format(url)
|
||||
)
|
||||
# Remove the vcs prefix.
|
||||
scheme = scheme.split('+', 1)[1]
|
||||
netloc, user_pass = self.get_netloc_and_auth(netloc, scheme)
|
||||
rev = None
|
||||
if '@' in path:
|
||||
path, rev = path.rsplit('@', 1)
|
||||
url = urllib_parse.urlunsplit((scheme, netloc, path, query, ''))
|
||||
return url, rev
|
||||
return url, rev, user_pass
|
||||
|
||||
def get_url_rev_args(self, url):
|
||||
def make_rev_args(self, username, password):
|
||||
"""
|
||||
Return the URL and RevOptions "extra arguments" to use in obtain(),
|
||||
as a tuple (url, extra_args).
|
||||
Return the RevOptions "extra arguments" to use in obtain().
|
||||
"""
|
||||
return url, []
|
||||
return []
|
||||
|
||||
def get_url_rev_options(self, url):
|
||||
"""
|
||||
Return the URL and RevOptions object to use in obtain() and in
|
||||
some cases export(), as a tuple (url, rev_options).
|
||||
"""
|
||||
url, rev = self.get_url_rev(url)
|
||||
url, extra_args = self.get_url_rev_args(url)
|
||||
url, rev, user_pass = self.get_url_rev_and_auth(url)
|
||||
username, password = user_pass
|
||||
extra_args = self.make_rev_args(username, password)
|
||||
rev_options = self.make_rev_options(rev, extra_args=extra_args)
|
||||
|
||||
return url, rev_options
|
||||
|
||||
def get_info(self, location):
|
||||
"""
|
||||
Returns (url, revision), where both are strings
|
||||
"""
|
||||
assert not location.rstrip('/').endswith(self.dirname), \
|
||||
'Bad directory: %s' % location
|
||||
return self.get_url(location), self.get_revision(location)
|
||||
|
||||
def normalize_url(self, url):
|
||||
"""
|
||||
Normalize a URL for comparison by unquoting it and removing any
|
||||
@@ -291,7 +299,7 @@ class VersionControl(object):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def update(self, dest, rev_options):
|
||||
def update(self, dest, url, rev_options):
|
||||
"""
|
||||
Update an already-existing repo to the given ``rev_options``.
|
||||
|
||||
@@ -341,7 +349,7 @@ class VersionControl(object):
|
||||
self.repo_name,
|
||||
rev_display,
|
||||
)
|
||||
self.update(dest, rev_options)
|
||||
self.update(dest, url, rev_options)
|
||||
else:
|
||||
logger.info('Skipping because already up-to-date.')
|
||||
return
|
||||
@@ -421,8 +429,6 @@ class VersionControl(object):
|
||||
def get_url(self, location):
|
||||
"""
|
||||
Return the url used at location
|
||||
|
||||
This is used in get_info() and obtain().
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ import os
|
||||
from pipenv.patched.notpip._vendor.six.moves.urllib import parse as urllib_parse
|
||||
|
||||
from pipenv.patched.notpip._internal.download import path_to_url
|
||||
from pipenv.patched.notpip._internal.utils.misc import display_path, rmtree
|
||||
from pipenv.patched.notpip._internal.utils.misc import (
|
||||
display_path, make_vcs_requirement_url, rmtree,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.utils.temp_dir import TempDirectory
|
||||
from pipenv.patched.notpip._internal.vcs import VersionControl, vcs
|
||||
|
||||
@@ -62,16 +64,16 @@ class Bazaar(VersionControl):
|
||||
def switch(self, dest, url, rev_options):
|
||||
self.run_command(['switch', url], cwd=dest)
|
||||
|
||||
def update(self, dest, rev_options):
|
||||
def update(self, dest, url, rev_options):
|
||||
cmd_args = ['pull', '-q'] + rev_options.to_args()
|
||||
self.run_command(cmd_args, cwd=dest)
|
||||
|
||||
def get_url_rev(self, url):
|
||||
def get_url_rev_and_auth(self, url):
|
||||
# hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it
|
||||
url, rev = super(Bazaar, self).get_url_rev(url)
|
||||
url, rev, user_pass = super(Bazaar, self).get_url_rev_and_auth(url)
|
||||
if url.startswith('ssh://'):
|
||||
url = 'bzr+' + url
|
||||
return url, rev
|
||||
return url, rev, user_pass
|
||||
|
||||
def get_url(self, location):
|
||||
urls = self.run_command(['info'], show_stdout=False, cwd=location)
|
||||
@@ -98,9 +100,9 @@ class Bazaar(VersionControl):
|
||||
return None
|
||||
if not repo.lower().startswith('bzr:'):
|
||||
repo = 'bzr+' + repo
|
||||
egg_project_name = dist.egg_name().split('-', 1)[0]
|
||||
current_rev = self.get_revision(location)
|
||||
return '%s@%s#egg=%s' % (repo, current_rev, egg_project_name)
|
||||
egg_project_name = dist.egg_name().split('-', 1)[0]
|
||||
return make_vcs_requirement_url(repo, current_rev, egg_project_name)
|
||||
|
||||
def is_commit_id_equal(self, dest, name):
|
||||
"""Always assume the versions don't match"""
|
||||
|
||||
@@ -8,9 +8,9 @@ from pipenv.patched.notpip._vendor.packaging.version import parse as parse_versi
|
||||
from pipenv.patched.notpip._vendor.six.moves.urllib import parse as urllib_parse
|
||||
from pipenv.patched.notpip._vendor.six.moves.urllib import request as urllib_request
|
||||
|
||||
from pipenv.patched.notpip._internal.compat import samefile
|
||||
from pipenv.patched.notpip._internal.exceptions import BadCommand
|
||||
from pipenv.patched.notpip._internal.utils.misc import display_path
|
||||
from pipenv.patched.notpip._internal.utils.compat import samefile
|
||||
from pipenv.patched.notpip._internal.utils.misc import display_path, make_vcs_requirement_url
|
||||
from pipenv.patched.notpip._internal.utils.temp_dir import TempDirectory
|
||||
from pipenv.patched.notpip._internal.vcs import VersionControl, vcs
|
||||
|
||||
@@ -77,6 +77,20 @@ class Git(VersionControl):
|
||||
version = '.'.join(version.split('.')[:3])
|
||||
return parse_version(version)
|
||||
|
||||
def get_branch(self, location):
|
||||
"""
|
||||
Return the current branch, or None if HEAD isn't at a branch
|
||||
(e.g. detached HEAD).
|
||||
"""
|
||||
args = ['rev-parse', '--abbrev-ref', 'HEAD']
|
||||
output = self.run_command(args, show_stdout=False, cwd=location)
|
||||
branch = output.strip()
|
||||
|
||||
if branch == 'HEAD':
|
||||
return None
|
||||
|
||||
return branch
|
||||
|
||||
def export(self, location):
|
||||
"""Export the Git repository at the url to the destination location"""
|
||||
if not location.endswith('/'):
|
||||
@@ -91,8 +105,8 @@ class Git(VersionControl):
|
||||
|
||||
def get_revision_sha(self, dest, rev):
|
||||
"""
|
||||
Return a commit hash for the given revision if it names a remote
|
||||
branch or tag. Otherwise, return None.
|
||||
Return (sha_or_none, is_branch), where sha_or_none is a commit hash
|
||||
if the revision names a remote branch or tag, otherwise None.
|
||||
|
||||
Args:
|
||||
dest: the repository directory.
|
||||
@@ -115,22 +129,30 @@ class Git(VersionControl):
|
||||
branch_ref = 'refs/remotes/origin/{}'.format(rev)
|
||||
tag_ref = 'refs/tags/{}'.format(rev)
|
||||
|
||||
return refs.get(branch_ref) or refs.get(tag_ref)
|
||||
sha = refs.get(branch_ref)
|
||||
if sha is not None:
|
||||
return (sha, True)
|
||||
|
||||
def check_rev_options(self, dest, rev_options):
|
||||
"""Check the revision options before checkout.
|
||||
sha = refs.get(tag_ref)
|
||||
|
||||
Returns a new RevOptions object for the SHA1 of the branch or tag
|
||||
if found.
|
||||
return (sha, False)
|
||||
|
||||
def resolve_revision(self, dest, url, rev_options):
|
||||
"""
|
||||
Resolve a revision to a new RevOptions object with the SHA1 of the
|
||||
branch, tag, or ref if found.
|
||||
|
||||
Args:
|
||||
rev_options: a RevOptions object.
|
||||
"""
|
||||
rev = rev_options.arg_rev
|
||||
sha = self.get_revision_sha(dest, rev)
|
||||
sha, is_branch = self.get_revision_sha(dest, rev)
|
||||
|
||||
if sha is not None:
|
||||
return rev_options.make_new(sha)
|
||||
rev_options = rev_options.make_new(sha)
|
||||
rev_options.branch_name = rev if is_branch else None
|
||||
|
||||
return rev_options
|
||||
|
||||
# Do not show a warning for the common case of something that has
|
||||
# the form of a Git commit hash.
|
||||
@@ -139,6 +161,19 @@ class Git(VersionControl):
|
||||
"Did not find branch or tag '%s', assuming revision or ref.",
|
||||
rev,
|
||||
)
|
||||
|
||||
if not rev.startswith('refs/'):
|
||||
return rev_options
|
||||
|
||||
# If it looks like a ref, we have to fetch it explicitly.
|
||||
self.run_command(
|
||||
['fetch', '-q', url] + rev_options.to_args(),
|
||||
cwd=dest,
|
||||
)
|
||||
# Change the revision to the SHA of the ref we fetched
|
||||
sha = self.get_revision(dest, rev='FETCH_HEAD')
|
||||
rev_options = rev_options.make_new(sha)
|
||||
|
||||
return rev_options
|
||||
|
||||
def is_commit_id_equal(self, dest, name):
|
||||
@@ -164,20 +199,22 @@ class Git(VersionControl):
|
||||
|
||||
if rev_options.rev:
|
||||
# Then a specific revision was requested.
|
||||
rev_options = self.check_rev_options(dest, rev_options)
|
||||
# Only do a checkout if the current commit id doesn't match
|
||||
# the requested revision.
|
||||
if not self.is_commit_id_equal(dest, rev_options.rev):
|
||||
rev = rev_options.rev
|
||||
# Only fetch the revision if it's a ref
|
||||
if rev.startswith('refs/'):
|
||||
self.run_command(
|
||||
['fetch', '-q', url] + rev_options.to_args(),
|
||||
cwd=dest,
|
||||
)
|
||||
# Change the revision to the SHA of the ref we fetched
|
||||
rev = 'FETCH_HEAD'
|
||||
self.run_command(['checkout', '-q', rev], cwd=dest)
|
||||
rev_options = self.resolve_revision(dest, url, rev_options)
|
||||
branch_name = getattr(rev_options, 'branch_name', None)
|
||||
if branch_name is None:
|
||||
# Only do a checkout if the current commit id doesn't match
|
||||
# the requested revision.
|
||||
if not self.is_commit_id_equal(dest, rev_options.rev):
|
||||
cmd_args = ['checkout', '-q'] + rev_options.to_args()
|
||||
self.run_command(cmd_args, cwd=dest)
|
||||
elif self.get_branch(dest) != branch_name:
|
||||
# Then a specific branch was requested, and that branch
|
||||
# is not yet checked out.
|
||||
track_branch = 'origin/{}'.format(branch_name)
|
||||
cmd_args = [
|
||||
'checkout', '-b', branch_name, '--track', track_branch,
|
||||
]
|
||||
self.run_command(cmd_args, cwd=dest)
|
||||
|
||||
#: repo may contain submodules
|
||||
self.update_submodules(dest)
|
||||
@@ -189,7 +226,7 @@ class Git(VersionControl):
|
||||
|
||||
self.update_submodules(dest)
|
||||
|
||||
def update(self, dest, rev_options):
|
||||
def update(self, dest, url, rev_options):
|
||||
# First fetch changes from the default remote
|
||||
if self.get_git_version() >= parse_version('1.9.0'):
|
||||
# fetch tags in addition to everything else
|
||||
@@ -197,7 +234,7 @@ class Git(VersionControl):
|
||||
else:
|
||||
self.run_command(['fetch', '-q'], cwd=dest)
|
||||
# Then reset to wanted revision (maybe even origin/master)
|
||||
rev_options = self.check_rev_options(dest, rev_options)
|
||||
rev_options = self.resolve_revision(dest, url, rev_options)
|
||||
cmd_args = ['reset', '--hard', '-q'] + rev_options.to_args()
|
||||
self.run_command(cmd_args, cwd=dest)
|
||||
#: update submodules
|
||||
@@ -218,9 +255,11 @@ class Git(VersionControl):
|
||||
url = found_remote.split(' ')[1]
|
||||
return url.strip()
|
||||
|
||||
def get_revision(self, location):
|
||||
def get_revision(self, location, rev=None):
|
||||
if rev is None:
|
||||
rev = 'HEAD'
|
||||
current_rev = self.run_command(
|
||||
['rev-parse', 'HEAD'], show_stdout=False, cwd=location,
|
||||
['rev-parse', rev], show_stdout=False, cwd=location,
|
||||
)
|
||||
return current_rev.strip()
|
||||
|
||||
@@ -255,17 +294,15 @@ class Git(VersionControl):
|
||||
repo = self.get_url(location)
|
||||
if not repo.lower().startswith('git:'):
|
||||
repo = 'git+' + repo
|
||||
egg_project_name = dist.egg_name().split('-', 1)[0]
|
||||
if not repo:
|
||||
return None
|
||||
current_rev = self.get_revision(location)
|
||||
req = '%s@%s#egg=%s' % (repo, current_rev, egg_project_name)
|
||||
subdirectory = self._get_subdirectory(location)
|
||||
if subdirectory:
|
||||
req += '&subdirectory=' + subdirectory
|
||||
egg_project_name = dist.egg_name().split('-', 1)[0]
|
||||
subdir = self._get_subdirectory(location)
|
||||
req = make_vcs_requirement_url(repo, current_rev, egg_project_name,
|
||||
subdir=subdir)
|
||||
|
||||
return req
|
||||
|
||||
def get_url_rev(self, url):
|
||||
def get_url_rev_and_auth(self, url):
|
||||
"""
|
||||
Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'.
|
||||
That's required because although they use SSH they sometimes don't
|
||||
@@ -275,12 +312,12 @@ class Git(VersionControl):
|
||||
if '://' not in url:
|
||||
assert 'file:' not in url
|
||||
url = url.replace('git+', 'git+ssh://')
|
||||
url, rev = super(Git, self).get_url_rev(url)
|
||||
url, rev, user_pass = super(Git, self).get_url_rev_and_auth(url)
|
||||
url = url.replace('ssh://', '')
|
||||
else:
|
||||
url, rev = super(Git, self).get_url_rev(url)
|
||||
url, rev, user_pass = super(Git, self).get_url_rev_and_auth(url)
|
||||
|
||||
return url, rev
|
||||
return url, rev, user_pass
|
||||
|
||||
def update_submodules(self, location):
|
||||
if not os.path.exists(os.path.join(location, '.gitmodules')):
|
||||
|
||||
@@ -6,7 +6,7 @@ import os
|
||||
from pipenv.patched.notpip._vendor.six.moves import configparser
|
||||
|
||||
from pipenv.patched.notpip._internal.download import path_to_url
|
||||
from pipenv.patched.notpip._internal.utils.misc import display_path
|
||||
from pipenv.patched.notpip._internal.utils.misc import display_path, make_vcs_requirement_url
|
||||
from pipenv.patched.notpip._internal.utils.temp_dir import TempDirectory
|
||||
from pipenv.patched.notpip._internal.vcs import VersionControl, vcs
|
||||
|
||||
@@ -59,7 +59,7 @@ class Mercurial(VersionControl):
|
||||
cmd_args = ['update', '-q'] + rev_options.to_args()
|
||||
self.run_command(cmd_args, cwd=dest)
|
||||
|
||||
def update(self, dest, rev_options):
|
||||
def update(self, dest, url, rev_options):
|
||||
self.run_command(['pull', '-q'], cwd=dest)
|
||||
cmd_args = ['update', '-q'] + rev_options.to_args()
|
||||
self.run_command(cmd_args, cwd=dest)
|
||||
@@ -88,11 +88,10 @@ class Mercurial(VersionControl):
|
||||
repo = self.get_url(location)
|
||||
if not repo.lower().startswith('hg:'):
|
||||
repo = 'hg+' + repo
|
||||
egg_project_name = dist.egg_name().split('-', 1)[0]
|
||||
if not repo:
|
||||
return None
|
||||
current_rev_hash = self.get_revision_hash(location)
|
||||
return '%s@%s#egg=%s' % (repo, current_rev_hash, egg_project_name)
|
||||
egg_project_name = dist.egg_name().split('-', 1)[0]
|
||||
return make_vcs_requirement_url(repo, current_rev_hash,
|
||||
egg_project_name)
|
||||
|
||||
def is_commit_id_equal(self, dest, name):
|
||||
"""Always assume the versions don't match"""
|
||||
|
||||
@@ -4,17 +4,15 @@ import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
from pipenv.patched.notpip._vendor.six.moves.urllib import parse as urllib_parse
|
||||
|
||||
from pipenv.patched.notpip._internal.index import Link
|
||||
from pipenv.patched.notpip._internal.models.link import Link
|
||||
from pipenv.patched.notpip._internal.utils.logging import indent_log
|
||||
from pipenv.patched.notpip._internal.utils.misc import display_path, remove_auth_from_url, rmtree
|
||||
from pipenv.patched.notpip._internal.utils.misc import (
|
||||
display_path, make_vcs_requirement_url, rmtree, split_auth_from_netloc,
|
||||
)
|
||||
from pipenv.patched.notpip._internal.vcs import VersionControl, vcs
|
||||
|
||||
_svn_xml_url_re = re.compile('url="([^"]+)"')
|
||||
_svn_rev_re = re.compile(r'committed-rev="(\d+)"')
|
||||
_svn_url_re = re.compile(r'URL: (.+)')
|
||||
_svn_revision_re = re.compile(r'Revision: (.+)')
|
||||
_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"')
|
||||
_svn_info_xml_url_re = re.compile(r'<url>(.*)</url>')
|
||||
|
||||
@@ -31,34 +29,6 @@ class Subversion(VersionControl):
|
||||
def get_base_rev_args(self, rev):
|
||||
return ['-r', rev]
|
||||
|
||||
def get_info(self, location):
|
||||
"""Returns (url, revision), where both are strings"""
|
||||
assert not location.rstrip('/').endswith(self.dirname), \
|
||||
'Bad directory: %s' % location
|
||||
output = self.run_command(
|
||||
['info', location],
|
||||
show_stdout=False,
|
||||
extra_environ={'LANG': 'C'},
|
||||
)
|
||||
match = _svn_url_re.search(output)
|
||||
if not match:
|
||||
logger.warning(
|
||||
'Cannot determine URL of svn checkout %s',
|
||||
display_path(location),
|
||||
)
|
||||
logger.debug('Output that cannot be parsed: \n%s', output)
|
||||
return None, None
|
||||
url = match.group(1).strip()
|
||||
match = _svn_revision_re.search(output)
|
||||
if not match:
|
||||
logger.warning(
|
||||
'Cannot determine revision of svn checkout %s',
|
||||
display_path(location),
|
||||
)
|
||||
logger.debug('Output that cannot be parsed: \n%s', output)
|
||||
return url, None
|
||||
return url, match.group(1)
|
||||
|
||||
def export(self, location):
|
||||
"""Export the svn repository at the url to the destination location"""
|
||||
url, rev_options = self.get_url_rev_options(self.url)
|
||||
@@ -87,7 +57,7 @@ class Subversion(VersionControl):
|
||||
cmd_args = ['switch'] + rev_options.to_args() + [url, dest]
|
||||
self.run_command(cmd_args)
|
||||
|
||||
def update(self, dest, rev_options):
|
||||
def update(self, dest, url, rev_options):
|
||||
cmd_args = ['update'] + rev_options.to_args() + [dest]
|
||||
self.run_command(cmd_args)
|
||||
|
||||
@@ -132,18 +102,34 @@ class Subversion(VersionControl):
|
||||
revision = max(revision, localrev)
|
||||
return revision
|
||||
|
||||
def get_url_rev(self, url):
|
||||
def get_netloc_and_auth(self, netloc, scheme):
|
||||
"""
|
||||
This override allows the auth information to be passed to svn via the
|
||||
--username and --password options instead of via the URL.
|
||||
"""
|
||||
if scheme == 'ssh':
|
||||
# The --username and --password options can't be used for
|
||||
# svn+ssh URLs, so keep the auth information in the URL.
|
||||
return super(Subversion, self).get_netloc_and_auth(
|
||||
netloc, scheme)
|
||||
|
||||
return split_auth_from_netloc(netloc)
|
||||
|
||||
def get_url_rev_and_auth(self, url):
|
||||
# hotfix the URL scheme after removing svn+ from svn+ssh:// readd it
|
||||
url, rev = super(Subversion, self).get_url_rev(url)
|
||||
url, rev, user_pass = super(Subversion, self).get_url_rev_and_auth(url)
|
||||
if url.startswith('ssh://'):
|
||||
url = 'svn+' + url
|
||||
return url, rev
|
||||
return url, rev, user_pass
|
||||
|
||||
def get_url_rev_args(self, url):
|
||||
extra_args = get_rev_options_args(url)
|
||||
url = remove_auth_from_url(url)
|
||||
def make_rev_args(self, username, password):
|
||||
extra_args = []
|
||||
if username:
|
||||
extra_args += ['--username', username]
|
||||
if password:
|
||||
extra_args += ['--password', password]
|
||||
|
||||
return url, extra_args
|
||||
return extra_args
|
||||
|
||||
def get_url(self, location):
|
||||
# In cases where the source is in a subdirectory, not alongside
|
||||
@@ -213,42 +199,15 @@ class Subversion(VersionControl):
|
||||
repo = self.get_url(location)
|
||||
if repo is None:
|
||||
return None
|
||||
repo = 'svn+' + repo
|
||||
rev = self.get_revision(location)
|
||||
# FIXME: why not project name?
|
||||
egg_project_name = dist.egg_name().split('-', 1)[0]
|
||||
rev = self.get_revision(location)
|
||||
return 'svn+%s@%s#egg=%s' % (repo, rev, egg_project_name)
|
||||
return make_vcs_requirement_url(repo, rev, egg_project_name)
|
||||
|
||||
def is_commit_id_equal(self, dest, name):
|
||||
"""Always assume the versions don't match"""
|
||||
return False
|
||||
|
||||
|
||||
def get_rev_options_args(url):
|
||||
"""
|
||||
Return the extra arguments to pass to RevOptions.
|
||||
"""
|
||||
r = urllib_parse.urlsplit(url)
|
||||
if hasattr(r, 'username'):
|
||||
# >= Python-2.5
|
||||
username, password = r.username, r.password
|
||||
else:
|
||||
netloc = r[1]
|
||||
if '@' in netloc:
|
||||
auth = netloc.split('@')[0]
|
||||
if ':' in auth:
|
||||
username, password = auth.split(':', 1)
|
||||
else:
|
||||
username, password = auth, None
|
||||
else:
|
||||
username, password = None, None
|
||||
|
||||
extra_args = []
|
||||
if username:
|
||||
extra_args += ['--username', username]
|
||||
if password:
|
||||
extra_args += ['--password', password]
|
||||
|
||||
return extra_args
|
||||
|
||||
|
||||
vcs.register(Subversion)
|
||||
|
||||
@@ -167,7 +167,8 @@ def message_about_scripts_not_on_PATH(scripts):
|
||||
]
|
||||
# If an executable sits with sys.executable, we don't warn for it.
|
||||
# This covers the case of venv invocations without activating the venv.
|
||||
not_warn_dirs.append(os.path.normcase(os.path.dirname(sys.executable)))
|
||||
executable_loc = os.environ.get("PIP_PYTHON_PATH", sys.executable)
|
||||
not_warn_dirs.append(os.path.normcase(os.path.dirname(executable_loc)))
|
||||
warn_for = {
|
||||
parent_dir: scripts for parent_dir, scripts in grouped_by_dir.items()
|
||||
if os.path.normcase(parent_dir) not in not_warn_dirs
|
||||
@@ -475,7 +476,7 @@ if __name__ == '__main__':
|
||||
if warn_script_location:
|
||||
msg = message_about_scripts_not_on_PATH(generated_console_scripts)
|
||||
if msg is not None:
|
||||
logger.warn(msg)
|
||||
logger.warning(msg)
|
||||
|
||||
if len(gui) > 0:
|
||||
generated.extend(
|
||||
@@ -500,16 +501,19 @@ if __name__ == '__main__':
|
||||
with open_for_csv(temp_record, 'w+') as record_out:
|
||||
reader = csv.reader(record_in)
|
||||
writer = csv.writer(record_out)
|
||||
outrows = []
|
||||
for row in reader:
|
||||
row[0] = installed.pop(row[0], row[0])
|
||||
if row[0] in changed:
|
||||
row[1], row[2] = rehash(row[0])
|
||||
writer.writerow(row)
|
||||
outrows.append(tuple(row))
|
||||
for f in generated:
|
||||
digest, length = rehash(f)
|
||||
writer.writerow((normpath(f, lib_dir), digest, length))
|
||||
outrows.append((normpath(f, lib_dir), digest, length))
|
||||
for f in installed:
|
||||
writer.writerow((installed[f], '', ''))
|
||||
outrows.append((installed[f], '', ''))
|
||||
for row in sorted(outrows):
|
||||
writer.writerow(row)
|
||||
shutil.move(temp_record, record)
|
||||
|
||||
|
||||
@@ -664,8 +668,9 @@ class WheelBuilder(object):
|
||||
# isolating. Currently, it breaks Python in virtualenvs, because it
|
||||
# relies on site.py to find parts of the standard library outside the
|
||||
# virtualenv.
|
||||
executable_loc = os.environ.get('PIP_PYTHON_PATH', sys.executable)
|
||||
return [
|
||||
os.environ.get('PIP_PYTHON_PATH', sys.executable), '-u', '-c',
|
||||
executable_loc, '-u', '-c',
|
||||
SETUPTOOLS_SHIM % req.setup_py
|
||||
] + list(self.global_options)
|
||||
|
||||
@@ -710,6 +715,7 @@ class WheelBuilder(object):
|
||||
:return: True if all the wheels built correctly.
|
||||
"""
|
||||
from pipenv.patched.notpip._internal import index
|
||||
from pipenv.patched.notpip._internal.models.link import Link
|
||||
|
||||
building_is_possible = self._wheel_dir or (
|
||||
autobuilding and self.wheel_cache.cache_dir
|
||||
@@ -717,6 +723,7 @@ class WheelBuilder(object):
|
||||
assert building_is_possible
|
||||
|
||||
buildset = []
|
||||
format_control = self.finder.format_control
|
||||
for req in requirements:
|
||||
if req.constraint:
|
||||
continue
|
||||
@@ -740,8 +747,7 @@ class WheelBuilder(object):
|
||||
if index.egg_info_matches(base, None, link) is None:
|
||||
# E.g. local directory. Build wheel just for this run.
|
||||
ephem_cache = True
|
||||
if "binary" not in index.fmt_ctl_formats(
|
||||
self.finder.format_control,
|
||||
if "binary" not in format_control.get_allowed_formats(
|
||||
canonicalize_name(req.name)):
|
||||
logger.info(
|
||||
"Skipping bdist_wheel for %s, due to binaries "
|
||||
@@ -802,7 +808,7 @@ class WheelBuilder(object):
|
||||
self.preparer.build_dir
|
||||
)
|
||||
# Update the link for this.
|
||||
req.link = index.Link(path_to_url(wheel_file))
|
||||
req.link = Link(path_to_url(wheel_file))
|
||||
assert req.link.is_wheel
|
||||
# extract the wheel into the dir
|
||||
unpack_url(
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
from .core import where, old_where
|
||||
|
||||
__version__ = "2018.04.16"
|
||||
__version__ = "2018.08.24"
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
from certifi import where
|
||||
from pipenv.patched.notpip._vendor.certifi import where
|
||||
print(where())
|
||||
|
||||
@@ -3692,169 +3692,6 @@ lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof
|
||||
TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Certplus Root CA G1 O=Certplus
|
||||
# Subject: CN=Certplus Root CA G1 O=Certplus
|
||||
# Label: "Certplus Root CA G1"
|
||||
# Serial: 1491911565779898356709731176965615564637713
|
||||
# MD5 Fingerprint: 7f:09:9c:f7:d9:b9:5c:69:69:56:d5:37:3e:14:0d:42
|
||||
# SHA1 Fingerprint: 22:fd:d0:b7:fd:a2:4e:0d:ac:49:2c:a0:ac:a6:7b:6a:1f:e3:f7:66
|
||||
# SHA256 Fingerprint: 15:2a:40:2b:fc:df:2c:d5:48:05:4d:22:75:b3:9c:7f:ca:3e:c0:97:80:78:b0:f0:ea:76:e5:61:a6:c7:43:3e
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUA
|
||||
MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy
|
||||
dHBsdXMgUm9vdCBDQSBHMTAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBa
|
||||
MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy
|
||||
dHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
|
||||
ANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHNr49a
|
||||
iZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt
|
||||
6kuJPKNxQv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP
|
||||
0FG7Yn2ksYyy/yARujVjBYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f
|
||||
6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTvLRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDE
|
||||
EW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2z4QTd28n6v+WZxcIbekN
|
||||
1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc4nBvCGrc
|
||||
h2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCT
|
||||
mehd4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV
|
||||
4EJQeIQEQWGw9CEjjy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPO
|
||||
WftwenMGE9nTdDckQQoRb5fc5+R+ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1Ud
|
||||
DwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSowcCbkahDFXxd
|
||||
Bie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHYlwuBsTANBgkq
|
||||
hkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh
|
||||
66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7
|
||||
/SMNkPX0XtPGYX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BS
|
||||
S7CTKtQ+FjPlnsZlFT5kOwQ/2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j
|
||||
2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F6ALEUz65noe8zDUa3qHpimOHZR4R
|
||||
Kttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilXCNQ314cnrUlZp5Gr
|
||||
RHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWetUNy
|
||||
6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEV
|
||||
V/xuZDDCVRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5
|
||||
g4VCXA9DO2pJNdWY9BW/+mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl
|
||||
++O/QmueD6i9a5jc2NvLi6Td11n0bt3+qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Certplus Root CA G2 O=Certplus
|
||||
# Subject: CN=Certplus Root CA G2 O=Certplus
|
||||
# Label: "Certplus Root CA G2"
|
||||
# Serial: 1492087096131536844209563509228951875861589
|
||||
# MD5 Fingerprint: a7:ee:c4:78:2d:1b:ee:2d:b9:29:ce:d6:a7:96:32:31
|
||||
# SHA1 Fingerprint: 4f:65:8e:1f:e9:06:d8:28:02:e9:54:47:41:c9:54:25:5d:69:cc:1a
|
||||
# SHA256 Fingerprint: 6c:c0:50:41:e6:44:5e:74:69:6c:4c:fb:c9:f8:0f:54:3b:7e:ab:bb:44:b4:ce:6f:78:7c:6a:99:71:c4:2f:17
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4x
|
||||
CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs
|
||||
dXMgUm9vdCBDQSBHMjAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4x
|
||||
CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs
|
||||
dXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABM0PW1aC3/BFGtat
|
||||
93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uNAm8x
|
||||
Ik0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0P
|
||||
AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwj
|
||||
FNiPwyCrKGBZMB8GA1UdIwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqG
|
||||
SM49BAMDA2gAMGUCMHD+sAvZ94OX7PNVHdTcswYO/jOYnYs5kGuUIe22113WTNch
|
||||
p+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjlvPl5adytRSv3tjFzzAal
|
||||
U5ORGpOucGpnutee5WEaXw==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=OpenTrust Root CA G1 O=OpenTrust
|
||||
# Subject: CN=OpenTrust Root CA G1 O=OpenTrust
|
||||
# Label: "OpenTrust Root CA G1"
|
||||
# Serial: 1492036577811947013770400127034825178844775
|
||||
# MD5 Fingerprint: 76:00:cc:81:29:cd:55:5e:88:6a:7a:2e:f7:4d:39:da
|
||||
# SHA1 Fingerprint: 79:91:e8:34:f7:e2:ee:dd:08:95:01:52:e9:55:2d:14:e9:58:d5:7e
|
||||
# SHA256 Fingerprint: 56:c7:71:28:d9:8c:18:d9:1b:4c:fd:ff:bc:25:ee:91:03:d4:75:8e:a2:ab:ad:82:6a:90:f3:45:7d:46:0e:b4
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUA
|
||||
MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w
|
||||
ZW5UcnVzdCBSb290IENBIEcxMB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAw
|
||||
MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU
|
||||
T3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
||||
AoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7faYp6b
|
||||
wiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX
|
||||
/uMftk87ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR0
|
||||
77F9jAHiOH3BX2pfJLKOYheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGP
|
||||
uY4zbGneWK2gDqdkVBFpRGZPTBKnjix9xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLx
|
||||
p2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO9z0M+Yo0FMT7MzUj8czx
|
||||
Kselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq3ywgsNw2
|
||||
TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+W
|
||||
G+Oin6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPw
|
||||
vFEVVJSmdz7QdFG9URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYY
|
||||
EQRVzXR7z2FwefR7LFxckvzluFqrTJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAO
|
||||
BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUl0YhVyE1
|
||||
2jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/PxN3DlCPaTKbYw
|
||||
DQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E
|
||||
PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kf
|
||||
gLMtMrpkZ2CvuVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbS
|
||||
FXJfLkur1J1juONI5f6ELlgKn0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0
|
||||
V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLhX4SPgPL0DTatdrOjteFkdjpY3H1P
|
||||
XlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80nR14SohWZ25g/4/I
|
||||
i+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcmGS3t
|
||||
TAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L91
|
||||
09S5zvE/bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/Ky
|
||||
Pu1svf0OnWZzsD2097+o4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJ
|
||||
AwSQiumPv+i2tCqjI40cHLI5kqiPAlxAOXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj
|
||||
1oxx
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=OpenTrust Root CA G2 O=OpenTrust
|
||||
# Subject: CN=OpenTrust Root CA G2 O=OpenTrust
|
||||
# Label: "OpenTrust Root CA G2"
|
||||
# Serial: 1492012448042702096986875987676935573415441
|
||||
# MD5 Fingerprint: 57:24:b6:59:24:6b:ae:c8:fe:1c:0c:20:f2:c0:4e:eb
|
||||
# SHA1 Fingerprint: 79:5f:88:60:c5:ab:7c:3d:92:e6:cb:f4:8d:e1:45:cd:11:ef:60:0b
|
||||
# SHA256 Fingerprint: 27:99:58:29:fe:6a:75:15:c1:bf:e8:48:f9:c4:76:1d:b1:6c:22:59:29:25:7b:f4:0d:08:94:f2:9e:a8:ba:f2
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUA
|
||||
MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w
|
||||
ZW5UcnVzdCBSb290IENBIEcyMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAw
|
||||
MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU
|
||||
T3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
||||
AoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+Ntmh
|
||||
/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78e
|
||||
CbY2albz4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/6
|
||||
1UWY0jUJ9gNDlP7ZvyCVeYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fE
|
||||
FY8ElggGQgT4hNYdvJGmQr5J1WqIP7wtUdGejeBSzFfdNTVY27SPJIjki9/ca1TS
|
||||
gSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz3GIZ38i1MH/1PCZ1Eb3X
|
||||
G7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj3CzMpSZy
|
||||
YhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaH
|
||||
vGOz9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4
|
||||
t/bQWVyJ98LVtZR00dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/
|
||||
gh7PU3+06yzbXfZqfUAkBXKJOAGTy3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAO
|
||||
BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUajn6QiL3
|
||||
5okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59M4PLuG53hq8w
|
||||
DQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz
|
||||
Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0
|
||||
nXGEL8pZ0keImUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qT
|
||||
RmTFAHneIWv2V6CG1wZy7HBGS4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpT
|
||||
wm+bREx50B1ws9efAvSyB7DH5fitIw6mVskpEndI2S9G/Tvw/HRwkqWOOAgfZDC2
|
||||
t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ6e18CL13zSdkzJTa
|
||||
TkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97krgCf2
|
||||
o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU
|
||||
3jg9CcCoSmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eA
|
||||
iN1nE28daCSLT7d0geX0YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14f
|
||||
WKGVyasvc0rQLW6aWQ9VGHgtPFGml4vmu7JwqkwR3v98KzfUetF3NI/n+UL3PIEM
|
||||
S1IK
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=OpenTrust Root CA G3 O=OpenTrust
|
||||
# Subject: CN=OpenTrust Root CA G3 O=OpenTrust
|
||||
# Label: "OpenTrust Root CA G3"
|
||||
# Serial: 1492104908271485653071219941864171170455615
|
||||
# MD5 Fingerprint: 21:37:b4:17:16:92:7b:67:46:70:a9:96:d7:a8:13:24
|
||||
# SHA1 Fingerprint: 6e:26:64:f3:56:bf:34:55:bf:d1:93:3f:7c:01:de:d8:13:da:8a:a6
|
||||
# SHA256 Fingerprint: b7:c3:62:31:70:6e:81:07:8c:36:7c:b8:96:19:8f:1e:32:08:dd:92:69:49:dd:8f:57:09:a4:10:f7:5b:62:92
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAx
|
||||
CzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5U
|
||||
cnVzdCBSb290IENBIEczMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFow
|
||||
QDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwUT3Bl
|
||||
blRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARK7liuTcpm
|
||||
3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5Bta1d
|
||||
oYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4G
|
||||
A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5
|
||||
DMlv4VBN0BBY3JWIbTAfBgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAK
|
||||
BggqhkjOPQQDAwNpADBmAjEAj6jcnboMBBf6Fek9LykBl7+BFjNAk2z8+e2AcG+q
|
||||
j9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta3U1fJAuwACEl74+nBCZx
|
||||
4nxp5V2a+EEfOzmTk51V6s2N8fvB
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=ISRG Root X1 O=Internet Security Research Group
|
||||
# Subject: CN=ISRG Root X1 O=Internet Security Research Group
|
||||
# Label: "ISRG Root X1"
|
||||
@@ -4398,3 +4235,66 @@ MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX
|
||||
ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg
|
||||
h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6
|
||||
# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6
|
||||
# Label: "GlobalSign Root CA - R6"
|
||||
# Serial: 1417766617973444989252670301619537
|
||||
# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae
|
||||
# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1
|
||||
# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg
|
||||
MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh
|
||||
bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx
|
||||
MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET
|
||||
MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ
|
||||
KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI
|
||||
xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k
|
||||
ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD
|
||||
aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw
|
||||
LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw
|
||||
1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX
|
||||
k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2
|
||||
SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h
|
||||
bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n
|
||||
WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY
|
||||
rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce
|
||||
MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD
|
||||
AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu
|
||||
bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN
|
||||
nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt
|
||||
Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61
|
||||
55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj
|
||||
vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf
|
||||
cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz
|
||||
oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp
|
||||
nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs
|
||||
pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v
|
||||
JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R
|
||||
8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4
|
||||
5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed
|
||||
# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed
|
||||
# Label: "OISTE WISeKey Global Root GC CA"
|
||||
# Serial: 44084345621038548146064804565436152554
|
||||
# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23
|
||||
# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31
|
||||
# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw
|
||||
CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91
|
||||
bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg
|
||||
Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ
|
||||
BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu
|
||||
ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS
|
||||
b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni
|
||||
eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W
|
||||
p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E
|
||||
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T
|
||||
rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV
|
||||
57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg
|
||||
Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -12,10 +12,10 @@ __title__ = "packaging"
|
||||
__summary__ = "Core utilities for Python packages"
|
||||
__uri__ = "https://github.com/pypa/packaging"
|
||||
|
||||
__version__ = "17.1"
|
||||
__version__ = "18.0"
|
||||
|
||||
__author__ = "Donald Stufft and individual contributors"
|
||||
__email__ = "donald@stufft.io"
|
||||
|
||||
__license__ = "BSD or Apache License, Version 2.0"
|
||||
__copyright__ = "Copyright 2014-2016 %s" % __author__
|
||||
__copyright__ = "Copyright 2014-2018 %s" % __author__
|
||||
|
||||
@@ -92,16 +92,16 @@ class Requirement(object):
|
||||
try:
|
||||
req = REQUIREMENT.parseString(requirement_string)
|
||||
except ParseException as e:
|
||||
raise InvalidRequirement(
|
||||
"Invalid requirement, parse error at \"{0!r}\"".format(
|
||||
requirement_string[e.loc:e.loc + 8]))
|
||||
raise InvalidRequirement("Parse error at \"{0!r}\": {1}".format(
|
||||
requirement_string[e.loc:e.loc + 8], e.msg
|
||||
))
|
||||
|
||||
self.name = req.name
|
||||
if req.url:
|
||||
parsed_url = urlparse.urlparse(req.url)
|
||||
if not (parsed_url.scheme and parsed_url.netloc) or (
|
||||
not parsed_url.scheme and not parsed_url.netloc):
|
||||
raise InvalidRequirement("Invalid URL given")
|
||||
raise InvalidRequirement("Invalid URL: {0}".format(req.url))
|
||||
self.url = req.url
|
||||
else:
|
||||
self.url = None
|
||||
|
||||
@@ -503,7 +503,7 @@ class Specifier(_IndividualSpecifier):
|
||||
return False
|
||||
|
||||
# Ensure that we do not allow a local version of the version mentioned
|
||||
# in the specifier, which is techincally greater than, to match.
|
||||
# in the specifier, which is technically greater than, to match.
|
||||
if prospective.local is not None:
|
||||
if Version(prospective.base_version) == Version(spec.base_version):
|
||||
return False
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Matthias C. M. Troffaes
|
||||
Copyright (c) 2012-2014 Antoine Pitrou and contributors
|
||||
Copyright (c) 2017 Thomas Kluyver
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -10,14 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,4 @@
|
||||
"""Wrappers to build Python packages using PEP 517 hooks
|
||||
"""
|
||||
|
||||
__version__ = '0.2'
|
||||
@@ -0,0 +1,182 @@
|
||||
"""This is invoked in a subprocess to call the build backend hooks.
|
||||
|
||||
It expects:
|
||||
- Command line args: hook_name, control_dir
|
||||
- Environment variable: PEP517_BUILD_BACKEND=entry.point:spec
|
||||
- control_dir/input.json:
|
||||
- {"kwargs": {...}}
|
||||
|
||||
Results:
|
||||
- control_dir/output.json
|
||||
- {"return_val": ...}
|
||||
"""
|
||||
from glob import glob
|
||||
from importlib import import_module
|
||||
import os
|
||||
from os.path import join as pjoin
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
# This is run as a script, not a module, so it can't do a relative import
|
||||
import compat
|
||||
|
||||
def _build_backend():
|
||||
"""Find and load the build backend"""
|
||||
ep = os.environ['PEP517_BUILD_BACKEND']
|
||||
mod_path, _, obj_path = ep.partition(':')
|
||||
obj = import_module(mod_path)
|
||||
if obj_path:
|
||||
for path_part in obj_path.split('.'):
|
||||
obj = getattr(obj, path_part)
|
||||
return obj
|
||||
|
||||
def get_requires_for_build_wheel(config_settings):
|
||||
"""Invoke the optional get_requires_for_build_wheel hook
|
||||
|
||||
Returns [] if the hook is not defined.
|
||||
"""
|
||||
backend = _build_backend()
|
||||
try:
|
||||
hook = backend.get_requires_for_build_wheel
|
||||
except AttributeError:
|
||||
return []
|
||||
else:
|
||||
return hook(config_settings)
|
||||
|
||||
def prepare_metadata_for_build_wheel(metadata_directory, config_settings):
|
||||
"""Invoke optional prepare_metadata_for_build_wheel
|
||||
|
||||
Implements a fallback by building a wheel if the hook isn't defined.
|
||||
"""
|
||||
backend = _build_backend()
|
||||
try:
|
||||
hook = backend.prepare_metadata_for_build_wheel
|
||||
except AttributeError:
|
||||
return _get_wheel_metadata_from_wheel(backend, metadata_directory,
|
||||
config_settings)
|
||||
else:
|
||||
return hook(metadata_directory, config_settings)
|
||||
|
||||
WHEEL_BUILT_MARKER = 'PEP517_ALREADY_BUILT_WHEEL'
|
||||
|
||||
def _dist_info_files(whl_zip):
|
||||
"""Identify the .dist-info folder inside a wheel ZipFile."""
|
||||
res = []
|
||||
for path in whl_zip.namelist():
|
||||
m = re.match(r'[^/\\]+-[^/\\]+\.dist-info/', path)
|
||||
if m:
|
||||
res.append(path)
|
||||
if res:
|
||||
return res
|
||||
raise Exception("No .dist-info folder found in wheel")
|
||||
|
||||
def _get_wheel_metadata_from_wheel(backend, metadata_directory, config_settings):
|
||||
"""Build a wheel and extract the metadata from it.
|
||||
|
||||
Fallback for when the build backend does not define the 'get_wheel_metadata'
|
||||
hook.
|
||||
"""
|
||||
from zipfile import ZipFile
|
||||
whl_basename = backend.build_wheel(metadata_directory, config_settings)
|
||||
with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), 'wb'):
|
||||
pass # Touch marker file
|
||||
|
||||
whl_file = os.path.join(metadata_directory, whl_basename)
|
||||
with ZipFile(whl_file) as zipf:
|
||||
dist_info = _dist_info_files(zipf)
|
||||
zipf.extractall(path=metadata_directory, members=dist_info)
|
||||
return dist_info[0].split('/')[0]
|
||||
|
||||
def _find_already_built_wheel(metadata_directory):
|
||||
"""Check for a wheel already built during the get_wheel_metadata hook.
|
||||
"""
|
||||
if not metadata_directory:
|
||||
return None
|
||||
metadata_parent = os.path.dirname(metadata_directory)
|
||||
if not os.path.isfile(pjoin(metadata_parent, WHEEL_BUILT_MARKER)):
|
||||
return None
|
||||
|
||||
whl_files = glob(os.path.join(metadata_parent, '*.whl'))
|
||||
if not whl_files:
|
||||
print('Found wheel built marker, but no .whl files')
|
||||
return None
|
||||
if len(whl_files) > 1:
|
||||
print('Found multiple .whl files; unspecified behaviour. '
|
||||
'Will call build_wheel.')
|
||||
return None
|
||||
|
||||
# Exactly one .whl file
|
||||
return whl_files[0]
|
||||
|
||||
def build_wheel(wheel_directory, config_settings, metadata_directory=None):
|
||||
"""Invoke the mandatory build_wheel hook.
|
||||
|
||||
If a wheel was already built in the prepare_metadata_for_build_wheel fallback, this
|
||||
will copy it rather than rebuilding the wheel.
|
||||
"""
|
||||
prebuilt_whl = _find_already_built_wheel(metadata_directory)
|
||||
if prebuilt_whl:
|
||||
shutil.copy2(prebuilt_whl, wheel_directory)
|
||||
return os.path.basename(prebuilt_whl)
|
||||
|
||||
return _build_backend().build_wheel(wheel_directory, config_settings,
|
||||
metadata_directory)
|
||||
|
||||
|
||||
def get_requires_for_build_sdist(config_settings):
|
||||
"""Invoke the optional get_requires_for_build_wheel hook
|
||||
|
||||
Returns [] if the hook is not defined.
|
||||
"""
|
||||
backend = _build_backend()
|
||||
try:
|
||||
hook = backend.get_requires_for_build_sdist
|
||||
except AttributeError:
|
||||
return []
|
||||
else:
|
||||
return hook(config_settings)
|
||||
|
||||
class _DummyException(Exception):
|
||||
"""Nothing should ever raise this exception"""
|
||||
|
||||
class GotUnsupportedOperation(Exception):
|
||||
"""For internal use when backend raises UnsupportedOperation"""
|
||||
|
||||
def build_sdist(sdist_directory, config_settings):
|
||||
"""Invoke the mandatory build_sdist hook."""
|
||||
backend = _build_backend()
|
||||
try:
|
||||
return backend.build_sdist(sdist_directory, config_settings)
|
||||
except getattr(backend, 'UnsupportedOperation', _DummyException):
|
||||
raise GotUnsupportedOperation
|
||||
|
||||
HOOK_NAMES = {
|
||||
'get_requires_for_build_wheel',
|
||||
'prepare_metadata_for_build_wheel',
|
||||
'build_wheel',
|
||||
'get_requires_for_build_sdist',
|
||||
'build_sdist',
|
||||
}
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
sys.exit("Needs args: hook_name, control_dir")
|
||||
hook_name = sys.argv[1]
|
||||
control_dir = sys.argv[2]
|
||||
if hook_name not in HOOK_NAMES:
|
||||
sys.exit("Unknown hook: %s" % hook_name)
|
||||
hook = globals()[hook_name]
|
||||
|
||||
hook_input = compat.read_json(pjoin(control_dir, 'input.json'))
|
||||
|
||||
json_out = {'unsupported': False, 'return_val': None}
|
||||
try:
|
||||
json_out['return_val'] = hook(**hook_input['kwargs'])
|
||||
except GotUnsupportedOperation:
|
||||
json_out['unsupported'] = True
|
||||
|
||||
compat.write_json(json_out, pjoin(control_dir, 'output.json'), indent=2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,194 @@
|
||||
"""Check a project and backend by attempting to build using PEP 517 hooks.
|
||||
"""
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
from os.path import isfile, join as pjoin
|
||||
from pipenv.patched.notpip._vendor.pytoml import TomlError, load as toml_load
|
||||
import shutil
|
||||
from subprocess import CalledProcessError
|
||||
import sys
|
||||
import tarfile
|
||||
from tempfile import mkdtemp
|
||||
import zipfile
|
||||
|
||||
from .colorlog import enable_colourful_output
|
||||
from .envbuild import BuildEnvironment
|
||||
from .wrappers import Pep517HookCaller
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def check_build_sdist(hooks):
|
||||
with BuildEnvironment() as env:
|
||||
try:
|
||||
env.pip_install(hooks.build_sys_requires)
|
||||
log.info('Installed static build dependencies')
|
||||
except CalledProcessError:
|
||||
log.error('Failed to install static build dependencies')
|
||||
return False
|
||||
|
||||
try:
|
||||
reqs = hooks.get_requires_for_build_sdist({})
|
||||
log.info('Got build requires: %s', reqs)
|
||||
except:
|
||||
log.error('Failure in get_requires_for_build_sdist', exc_info=True)
|
||||
return False
|
||||
|
||||
try:
|
||||
env.pip_install(reqs)
|
||||
log.info('Installed dynamic build dependencies')
|
||||
except CalledProcessError:
|
||||
log.error('Failed to install dynamic build dependencies')
|
||||
return False
|
||||
|
||||
td = mkdtemp()
|
||||
log.info('Trying to build sdist in %s', td)
|
||||
try:
|
||||
try:
|
||||
filename = hooks.build_sdist(td, {})
|
||||
log.info('build_sdist returned %r', filename)
|
||||
except:
|
||||
log.info('Failure in build_sdist', exc_info=True)
|
||||
return False
|
||||
|
||||
if not filename.endswith('.tar.gz'):
|
||||
log.error("Filename %s doesn't have .tar.gz extension", filename)
|
||||
return False
|
||||
|
||||
path = pjoin(td, filename)
|
||||
if isfile(path):
|
||||
log.info("Output file %s exists", path)
|
||||
else:
|
||||
log.error("Output file %s does not exist", path)
|
||||
return False
|
||||
|
||||
if tarfile.is_tarfile(path):
|
||||
log.info("Output file is a tar file")
|
||||
else:
|
||||
log.error("Output file is not a tar file")
|
||||
return False
|
||||
|
||||
finally:
|
||||
shutil.rmtree(td)
|
||||
|
||||
return True
|
||||
|
||||
def check_build_wheel(hooks):
|
||||
with BuildEnvironment() as env:
|
||||
try:
|
||||
env.pip_install(hooks.build_sys_requires)
|
||||
log.info('Installed static build dependencies')
|
||||
except CalledProcessError:
|
||||
log.error('Failed to install static build dependencies')
|
||||
return False
|
||||
|
||||
try:
|
||||
reqs = hooks.get_requires_for_build_wheel({})
|
||||
log.info('Got build requires: %s', reqs)
|
||||
except:
|
||||
log.error('Failure in get_requires_for_build_sdist', exc_info=True)
|
||||
return False
|
||||
|
||||
try:
|
||||
env.pip_install(reqs)
|
||||
log.info('Installed dynamic build dependencies')
|
||||
except CalledProcessError:
|
||||
log.error('Failed to install dynamic build dependencies')
|
||||
return False
|
||||
|
||||
td = mkdtemp()
|
||||
log.info('Trying to build wheel in %s', td)
|
||||
try:
|
||||
try:
|
||||
filename = hooks.build_wheel(td, {})
|
||||
log.info('build_wheel returned %r', filename)
|
||||
except:
|
||||
log.info('Failure in build_wheel', exc_info=True)
|
||||
return False
|
||||
|
||||
if not filename.endswith('.whl'):
|
||||
log.error("Filename %s doesn't have .whl extension", filename)
|
||||
return False
|
||||
|
||||
path = pjoin(td, filename)
|
||||
if isfile(path):
|
||||
log.info("Output file %s exists", path)
|
||||
else:
|
||||
log.error("Output file %s does not exist", path)
|
||||
return False
|
||||
|
||||
if zipfile.is_zipfile(path):
|
||||
log.info("Output file is a zip file")
|
||||
else:
|
||||
log.error("Output file is not a zip file")
|
||||
return False
|
||||
|
||||
finally:
|
||||
shutil.rmtree(td)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def check(source_dir):
|
||||
pyproject = pjoin(source_dir, 'pyproject.toml')
|
||||
if isfile(pyproject):
|
||||
log.info('Found pyproject.toml')
|
||||
else:
|
||||
log.error('Missing pyproject.toml')
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(pyproject) as f:
|
||||
pyproject_data = toml_load(f)
|
||||
# Ensure the mandatory data can be loaded
|
||||
buildsys = pyproject_data['build-system']
|
||||
requires = buildsys['requires']
|
||||
backend = buildsys['build-backend']
|
||||
log.info('Loaded pyproject.toml')
|
||||
except (TomlError, KeyError):
|
||||
log.error("Invalid pyproject.toml", exc_info=True)
|
||||
return False
|
||||
|
||||
hooks = Pep517HookCaller(source_dir, backend)
|
||||
|
||||
sdist_ok = check_build_sdist(hooks)
|
||||
wheel_ok = check_build_wheel(hooks)
|
||||
|
||||
if not sdist_ok:
|
||||
log.warning('Sdist checks failed; scroll up to see')
|
||||
if not wheel_ok:
|
||||
log.warning('Wheel checks failed')
|
||||
|
||||
return sdist_ok
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('source_dir',
|
||||
help="A directory containing pyproject.toml")
|
||||
args = ap.parse_args(argv)
|
||||
|
||||
enable_colourful_output()
|
||||
|
||||
ok = check(args.source_dir)
|
||||
|
||||
if ok:
|
||||
print(ansi('Checks passed', 'green'))
|
||||
else:
|
||||
print(ansi('Checks failed', 'red'))
|
||||
sys.exit(1)
|
||||
|
||||
ansi_codes = {
|
||||
'reset': '\x1b[0m',
|
||||
'bold': '\x1b[1m',
|
||||
'red': '\x1b[31m',
|
||||
'green': '\x1b[32m',
|
||||
}
|
||||
def ansi(s, attr):
|
||||
if os.name != 'nt' and sys.stdout.isatty():
|
||||
return ansi_codes[attr] + str(s) + ansi_codes['reset']
|
||||
else:
|
||||
return str(s)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,110 @@
|
||||
"""Nicer log formatting with colours.
|
||||
|
||||
Code copied from Tornado, Apache licensed.
|
||||
"""
|
||||
# Copyright 2012 Facebook
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
try:
|
||||
import curses
|
||||
except ImportError:
|
||||
curses = None
|
||||
|
||||
def _stderr_supports_color():
|
||||
color = False
|
||||
if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
|
||||
try:
|
||||
curses.setupterm()
|
||||
if curses.tigetnum("colors") > 0:
|
||||
color = True
|
||||
except Exception:
|
||||
pass
|
||||
return color
|
||||
|
||||
class LogFormatter(logging.Formatter):
|
||||
"""Log formatter with colour support
|
||||
"""
|
||||
DEFAULT_COLORS = {
|
||||
logging.INFO: 2, # Green
|
||||
logging.WARNING: 3, # Yellow
|
||||
logging.ERROR: 1, # Red
|
||||
logging.CRITICAL: 1,
|
||||
}
|
||||
|
||||
def __init__(self, color=True, datefmt=None):
|
||||
r"""
|
||||
:arg bool color: Enables color support.
|
||||
:arg string fmt: Log message format.
|
||||
It will be applied to the attributes dict of log records. The
|
||||
text between ``%(color)s`` and ``%(end_color)s`` will be colored
|
||||
depending on the level if color support is on.
|
||||
:arg dict colors: color mappings from logging level to terminal color
|
||||
code
|
||||
:arg string datefmt: Datetime format.
|
||||
Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``.
|
||||
.. versionchanged:: 3.2
|
||||
Added ``fmt`` and ``datefmt`` arguments.
|
||||
"""
|
||||
logging.Formatter.__init__(self, datefmt=datefmt)
|
||||
self._colors = {}
|
||||
if color and _stderr_supports_color():
|
||||
# The curses module has some str/bytes confusion in
|
||||
# python3. Until version 3.2.3, most methods return
|
||||
# bytes, but only accept strings. In addition, we want to
|
||||
# output these strings with the logging module, which
|
||||
# works with unicode strings. The explicit calls to
|
||||
# unicode() below are harmless in python2 but will do the
|
||||
# right conversion in python 3.
|
||||
fg_color = (curses.tigetstr("setaf") or
|
||||
curses.tigetstr("setf") or "")
|
||||
if (3, 0) < sys.version_info < (3, 2, 3):
|
||||
fg_color = str(fg_color, "ascii")
|
||||
|
||||
for levelno, code in self.DEFAULT_COLORS.items():
|
||||
self._colors[levelno] = str(curses.tparm(fg_color, code), "ascii")
|
||||
self._normal = str(curses.tigetstr("sgr0"), "ascii")
|
||||
|
||||
scr = curses.initscr()
|
||||
self.termwidth = scr.getmaxyx()[1]
|
||||
curses.endwin()
|
||||
else:
|
||||
self._normal = ''
|
||||
# Default width is usually 80, but too wide is worse than too narrow
|
||||
self.termwidth = 70
|
||||
|
||||
def formatMessage(self, record):
|
||||
l = len(record.message)
|
||||
right_text = '{initial}-{name}'.format(initial=record.levelname[0],
|
||||
name=record.name)
|
||||
if l + len(right_text) < self.termwidth:
|
||||
space = ' ' * (self.termwidth - (l + len(right_text)))
|
||||
else:
|
||||
space = ' '
|
||||
|
||||
if record.levelno in self._colors:
|
||||
start_color = self._colors[record.levelno]
|
||||
end_color = self._normal
|
||||
else:
|
||||
start_color = end_color = ''
|
||||
|
||||
return record.message + space + start_color + right_text + end_color
|
||||
|
||||
def enable_colourful_output(level=logging.INFO):
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(LogFormatter())
|
||||
logging.root.addHandler(handler)
|
||||
logging.root.setLevel(level)
|
||||
@@ -0,0 +1,23 @@
|
||||
"""Handle reading and writing JSON in UTF-8, on Python 3 and 2."""
|
||||
import json
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
# Python 3
|
||||
def write_json(obj, path, **kwargs):
|
||||
with open(path, 'w', encoding='utf-8') as f:
|
||||
json.dump(obj, f, **kwargs)
|
||||
|
||||
def read_json(path):
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
else:
|
||||
# Python 2
|
||||
def write_json(obj, path, **kwargs):
|
||||
with open(path, 'wb') as f:
|
||||
json.dump(obj, f, encoding='utf-8', **kwargs)
|
||||
|
||||
def read_json(path):
|
||||
with open(path, 'rb') as f:
|
||||
return json.load(f)
|
||||
@@ -0,0 +1,150 @@
|
||||
"""Build wheels/sdists by installing build deps to a temporary environment.
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
from pipenv.patched.notpip._vendor import pytoml
|
||||
import shutil
|
||||
from subprocess import check_call
|
||||
import sys
|
||||
from sysconfig import get_paths
|
||||
from tempfile import mkdtemp
|
||||
|
||||
from .wrappers import Pep517HookCaller
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def _load_pyproject(source_dir):
|
||||
with open(os.path.join(source_dir, 'pyproject.toml')) as f:
|
||||
pyproject_data = pytoml.load(f)
|
||||
buildsys = pyproject_data['build-system']
|
||||
return buildsys['requires'], buildsys['build-backend']
|
||||
|
||||
|
||||
class BuildEnvironment(object):
|
||||
"""Context manager to install build deps in a simple temporary environment
|
||||
|
||||
Based on code I wrote for pip, which is MIT licensed.
|
||||
"""
|
||||
# Copyright (c) 2008-2016 The pip developers (see AUTHORS.txt file)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
path = None
|
||||
|
||||
def __init__(self, cleanup=True):
|
||||
self._cleanup = cleanup
|
||||
|
||||
def __enter__(self):
|
||||
self.path = mkdtemp(prefix='pep517-build-env-')
|
||||
log.info('Temporary build environment: %s', self.path)
|
||||
|
||||
self.save_path = os.environ.get('PATH', None)
|
||||
self.save_pythonpath = os.environ.get('PYTHONPATH', None)
|
||||
|
||||
install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix'
|
||||
install_dirs = get_paths(install_scheme, vars={
|
||||
'base': self.path,
|
||||
'platbase': self.path,
|
||||
})
|
||||
|
||||
scripts = install_dirs['scripts']
|
||||
if self.save_path:
|
||||
os.environ['PATH'] = scripts + os.pathsep + self.save_path
|
||||
else:
|
||||
os.environ['PATH'] = scripts + os.pathsep + os.defpath
|
||||
|
||||
if install_dirs['purelib'] == install_dirs['platlib']:
|
||||
lib_dirs = install_dirs['purelib']
|
||||
else:
|
||||
lib_dirs = install_dirs['purelib'] + os.pathsep + \
|
||||
install_dirs['platlib']
|
||||
if self.save_pythonpath:
|
||||
os.environ['PYTHONPATH'] = lib_dirs + os.pathsep + \
|
||||
self.save_pythonpath
|
||||
else:
|
||||
os.environ['PYTHONPATH'] = lib_dirs
|
||||
|
||||
return self
|
||||
|
||||
def pip_install(self, reqs):
|
||||
"""Install dependencies into this env by calling pip in a subprocess"""
|
||||
if not reqs:
|
||||
return
|
||||
log.info('Calling pip to install %s', reqs)
|
||||
check_call([sys.executable, '-m', 'pip', 'install', '--ignore-installed',
|
||||
'--prefix', self.path] + list(reqs))
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if self._cleanup and (self.path is not None) and os.path.isdir(self.path):
|
||||
shutil.rmtree(self.path)
|
||||
|
||||
if self.save_path is None:
|
||||
os.environ.pop('PATH', None)
|
||||
else:
|
||||
os.environ['PATH'] = self.save_path
|
||||
|
||||
if self.save_pythonpath is None:
|
||||
os.environ.pop('PYTHONPATH', None)
|
||||
else:
|
||||
os.environ['PYTHONPATH'] = self.save_pythonpath
|
||||
|
||||
def build_wheel(source_dir, wheel_dir, config_settings=None):
|
||||
"""Build a wheel from a source directory using PEP 517 hooks.
|
||||
|
||||
:param str source_dir: Source directory containing pyproject.toml
|
||||
:param str wheel_dir: Target directory to create wheel in
|
||||
:param dict config_settings: Options to pass to build backend
|
||||
|
||||
This is a blocking function which will run pip in a subprocess to install
|
||||
build requirements.
|
||||
"""
|
||||
if config_settings is None:
|
||||
config_settings = {}
|
||||
requires, backend = _load_pyproject(source_dir)
|
||||
hooks = Pep517HookCaller(source_dir, backend)
|
||||
|
||||
with BuildEnvironment() as env:
|
||||
env.pip_install(requires)
|
||||
reqs = hooks.get_requires_for_build_wheel(config_settings)
|
||||
env.pip_install(reqs)
|
||||
return hooks.build_wheel(wheel_dir, config_settings)
|
||||
|
||||
|
||||
def build_sdist(source_dir, sdist_dir, config_settings=None):
|
||||
"""Build an sdist from a source directory using PEP 517 hooks.
|
||||
|
||||
:param str source_dir: Source directory containing pyproject.toml
|
||||
:param str sdist_dir: Target directory to place sdist in
|
||||
:param dict config_settings: Options to pass to build backend
|
||||
|
||||
This is a blocking function which will run pip in a subprocess to install
|
||||
build requirements.
|
||||
"""
|
||||
if config_settings is None:
|
||||
config_settings = {}
|
||||
requires, backend = _load_pyproject(source_dir)
|
||||
hooks = Pep517HookCaller(source_dir, backend)
|
||||
|
||||
with BuildEnvironment() as env:
|
||||
env.pip_install(requires)
|
||||
reqs = hooks.get_requires_for_build_sdist(config_settings)
|
||||
env.pip_install(reqs)
|
||||
return hooks.build_sdist(sdist_dir, config_settings)
|
||||
@@ -0,0 +1,134 @@
|
||||
from contextlib import contextmanager
|
||||
import os
|
||||
from os.path import dirname, abspath, join as pjoin
|
||||
import shutil
|
||||
from subprocess import check_call
|
||||
import sys
|
||||
from tempfile import mkdtemp
|
||||
|
||||
from . import compat
|
||||
|
||||
_in_proc_script = pjoin(dirname(abspath(__file__)), '_in_process.py')
|
||||
|
||||
@contextmanager
|
||||
def tempdir():
|
||||
td = mkdtemp()
|
||||
try:
|
||||
yield td
|
||||
finally:
|
||||
shutil.rmtree(td)
|
||||
|
||||
class UnsupportedOperation(Exception):
|
||||
"""May be raised by build_sdist if the backend indicates that it can't."""
|
||||
|
||||
class Pep517HookCaller(object):
|
||||
"""A wrapper around a source directory to be built with a PEP 517 backend.
|
||||
|
||||
source_dir : The path to the source directory, containing pyproject.toml.
|
||||
backend : The build backend spec, as per PEP 517, from pyproject.toml.
|
||||
"""
|
||||
def __init__(self, source_dir, build_backend):
|
||||
self.source_dir = abspath(source_dir)
|
||||
self.build_backend = build_backend
|
||||
|
||||
def get_requires_for_build_wheel(self, config_settings=None):
|
||||
"""Identify packages required for building a wheel
|
||||
|
||||
Returns a list of dependency specifications, e.g.:
|
||||
["wheel >= 0.25", "setuptools"]
|
||||
|
||||
This does not include requirements specified in pyproject.toml.
|
||||
It returns the result of calling the equivalently named hook in a
|
||||
subprocess.
|
||||
"""
|
||||
return self._call_hook('get_requires_for_build_wheel', {
|
||||
'config_settings': config_settings
|
||||
})
|
||||
|
||||
def prepare_metadata_for_build_wheel(self, metadata_directory, config_settings=None):
|
||||
"""Prepare a *.dist-info folder with metadata for this project.
|
||||
|
||||
Returns the name of the newly created folder.
|
||||
|
||||
If the build backend defines a hook with this name, it will be called
|
||||
in a subprocess. If not, the backend will be asked to build a wheel,
|
||||
and the dist-info extracted from that.
|
||||
"""
|
||||
return self._call_hook('prepare_metadata_for_build_wheel', {
|
||||
'metadata_directory': abspath(metadata_directory),
|
||||
'config_settings': config_settings,
|
||||
})
|
||||
|
||||
def build_wheel(self, wheel_directory, config_settings=None, metadata_directory=None):
|
||||
"""Build a wheel from this project.
|
||||
|
||||
Returns the name of the newly created file.
|
||||
|
||||
In general, this will call the 'build_wheel' hook in the backend.
|
||||
However, if that was previously called by
|
||||
'prepare_metadata_for_build_wheel', and the same metadata_directory is
|
||||
used, the previously built wheel will be copied to wheel_directory.
|
||||
"""
|
||||
if metadata_directory is not None:
|
||||
metadata_directory = abspath(metadata_directory)
|
||||
return self._call_hook('build_wheel', {
|
||||
'wheel_directory': abspath(wheel_directory),
|
||||
'config_settings': config_settings,
|
||||
'metadata_directory': metadata_directory,
|
||||
})
|
||||
|
||||
def get_requires_for_build_sdist(self, config_settings=None):
|
||||
"""Identify packages required for building a wheel
|
||||
|
||||
Returns a list of dependency specifications, e.g.:
|
||||
["setuptools >= 26"]
|
||||
|
||||
This does not include requirements specified in pyproject.toml.
|
||||
It returns the result of calling the equivalently named hook in a
|
||||
subprocess.
|
||||
"""
|
||||
return self._call_hook('get_requires_for_build_sdist', {
|
||||
'config_settings': config_settings
|
||||
})
|
||||
|
||||
def build_sdist(self, sdist_directory, config_settings=None):
|
||||
"""Build an sdist from this project.
|
||||
|
||||
Returns the name of the newly created file.
|
||||
|
||||
This calls the 'build_sdist' backend hook in a subprocess.
|
||||
"""
|
||||
return self._call_hook('build_sdist', {
|
||||
'sdist_directory': abspath(sdist_directory),
|
||||
'config_settings': config_settings,
|
||||
})
|
||||
|
||||
|
||||
def _call_hook(self, hook_name, kwargs):
|
||||
env = os.environ.copy()
|
||||
|
||||
# On Python 2, pytoml returns Unicode values (which is correct) but the
|
||||
# environment passed to check_call needs to contain string values. We
|
||||
# convert here by encoding using ASCII (the backend can only contain
|
||||
# letters, digits and _, . and : characters, and will be used as a
|
||||
# Python identifier, so non-ASCII content is wrong on Python 2 in
|
||||
# any case).
|
||||
if sys.version_info[0] == 2:
|
||||
build_backend = self.build_backend.encode('ASCII')
|
||||
else:
|
||||
build_backend = self.build_backend
|
||||
|
||||
env['PEP517_BUILD_BACKEND'] = build_backend
|
||||
with tempdir() as td:
|
||||
compat.write_json({'kwargs': kwargs}, pjoin(td, 'input.json'),
|
||||
indent=2)
|
||||
|
||||
# Run the hook in a subprocess
|
||||
check_call([sys.executable, _in_proc_script, hook_name, td],
|
||||
cwd=self.source_dir, env=env)
|
||||
|
||||
data = compat.read_json(pjoin(td, 'output.json'))
|
||||
if data.get('unsupported'):
|
||||
raise UnsupportedOperation
|
||||
return data['return_val']
|
||||
|
||||
@@ -47,6 +47,11 @@ except ImportError:
|
||||
# Python 3.2 compatibility
|
||||
import imp as _imp
|
||||
|
||||
try:
|
||||
FileExistsError
|
||||
except NameError:
|
||||
FileExistsError = OSError
|
||||
|
||||
from pipenv.patched.notpip._vendor import six
|
||||
from pipenv.patched.notpip._vendor.six.moves import urllib, map, filter
|
||||
|
||||
@@ -78,8 +83,11 @@ __import__('pipenv.patched.notpip._vendor.packaging.requirements')
|
||||
__import__('pipenv.patched.notpip._vendor.packaging.markers')
|
||||
|
||||
|
||||
if (3, 0) < sys.version_info < (3, 3):
|
||||
raise RuntimeError("Python 3.3 or later is required")
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
if (3, 0) < sys.version_info < (3, 4):
|
||||
raise RuntimeError("Python 3.4 or later is required")
|
||||
|
||||
if six.PY2:
|
||||
# Those builtin exceptions are only defined in Python 3
|
||||
@@ -537,7 +545,7 @@ class IResourceProvider(IMetadataProvider):
|
||||
"""List of resource names in the directory (like ``os.listdir()``)"""
|
||||
|
||||
|
||||
class WorkingSet(object):
|
||||
class WorkingSet:
|
||||
"""A collection of active distributions on sys.path (or a similar list)"""
|
||||
|
||||
def __init__(self, entries=None):
|
||||
@@ -637,13 +645,12 @@ class WorkingSet(object):
|
||||
distributions in the working set, otherwise only ones matching
|
||||
both `group` and `name` are yielded (in distribution order).
|
||||
"""
|
||||
for dist in self:
|
||||
entries = dist.get_entry_map(group)
|
||||
if name is None:
|
||||
for ep in entries.values():
|
||||
yield ep
|
||||
elif name in entries:
|
||||
yield entries[name]
|
||||
return (
|
||||
entry
|
||||
for dist in self
|
||||
for entry in dist.get_entry_map(group).values()
|
||||
if name is None or name == entry.name
|
||||
)
|
||||
|
||||
def run_script(self, requires, script_name):
|
||||
"""Locate distribution for `requires` and run `script_name` script"""
|
||||
@@ -944,7 +951,7 @@ class _ReqExtras(dict):
|
||||
return not req.marker or any(extra_evals)
|
||||
|
||||
|
||||
class Environment(object):
|
||||
class Environment:
|
||||
"""Searchable snapshot of distributions on a search path"""
|
||||
|
||||
def __init__(
|
||||
@@ -959,7 +966,7 @@ class Environment(object):
|
||||
`platform` is an optional string specifying the name of the platform
|
||||
that platform-specific distributions must be compatible with. If
|
||||
unspecified, it defaults to the current platform. `python` is an
|
||||
optional string naming the desired version of Python (e.g. ``'3.3'``);
|
||||
optional string naming the desired version of Python (e.g. ``'3.6'``);
|
||||
it defaults to the current version.
|
||||
|
||||
You may explicitly set `platform` (and/or `python`) to ``None`` if you
|
||||
@@ -2087,7 +2094,12 @@ def _handle_ns(packageName, path_item):
|
||||
importer = get_importer(path_item)
|
||||
if importer is None:
|
||||
return None
|
||||
loader = importer.find_module(packageName)
|
||||
|
||||
# capture warnings due to #1111
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
loader = importer.find_module(packageName)
|
||||
|
||||
if loader is None:
|
||||
return None
|
||||
module = sys.modules.get(packageName)
|
||||
@@ -2132,12 +2144,13 @@ def _rebuild_mod_path(orig_path, package_name, module):
|
||||
parts = path_parts[:-module_parts]
|
||||
return safe_sys_path_index(_normalize_cached(os.sep.join(parts)))
|
||||
|
||||
if not isinstance(orig_path, list):
|
||||
# Is this behavior useful when module.__path__ is not a list?
|
||||
return
|
||||
new_path = sorted(orig_path, key=position_in_sys_path)
|
||||
new_path = [_normalize_cached(p) for p in new_path]
|
||||
|
||||
orig_path.sort(key=position_in_sys_path)
|
||||
module.__path__[:] = [_normalize_cached(p) for p in orig_path]
|
||||
if isinstance(module.__path__, list):
|
||||
module.__path__[:] = new_path
|
||||
else:
|
||||
module.__path__ = new_path
|
||||
|
||||
|
||||
def declare_namespace(packageName):
|
||||
@@ -2148,9 +2161,10 @@ def declare_namespace(packageName):
|
||||
if packageName in _namespace_packages:
|
||||
return
|
||||
|
||||
path, parent = sys.path, None
|
||||
if '.' in packageName:
|
||||
parent = '.'.join(packageName.split('.')[:-1])
|
||||
path = sys.path
|
||||
parent, _, _ = packageName.rpartition('.')
|
||||
|
||||
if parent:
|
||||
declare_namespace(parent)
|
||||
if parent not in _namespace_packages:
|
||||
__import__(parent)
|
||||
@@ -2161,7 +2175,7 @@ def declare_namespace(packageName):
|
||||
|
||||
# Track what packages are namespaces, so when new path items are added,
|
||||
# they can be updated
|
||||
_namespace_packages.setdefault(parent, []).append(packageName)
|
||||
_namespace_packages.setdefault(parent or None, []).append(packageName)
|
||||
_namespace_packages.setdefault(packageName, [])
|
||||
|
||||
for path_item in path:
|
||||
@@ -2279,7 +2293,7 @@ EGG_NAME = re.compile(
|
||||
).match
|
||||
|
||||
|
||||
class EntryPoint(object):
|
||||
class EntryPoint:
|
||||
"""Object representing an advertised importable object"""
|
||||
|
||||
def __init__(self, name, module_name, attrs=(), extras=(), dist=None):
|
||||
@@ -2433,7 +2447,7 @@ def _version_from_file(lines):
|
||||
return safe_version(value.strip()) or None
|
||||
|
||||
|
||||
class Distribution(object):
|
||||
class Distribution:
|
||||
"""Wrap an actual or potential sys.path entry w/metadata"""
|
||||
PKG_INFO = 'PKG-INFO'
|
||||
|
||||
@@ -3027,7 +3041,10 @@ def _bypass_ensure_directory(path):
|
||||
dirname, filename = split(path)
|
||||
if dirname and filename and not isdir(dirname):
|
||||
_bypass_ensure_directory(dirname)
|
||||
mkdir(dirname, 0o755)
|
||||
try:
|
||||
mkdir(dirname, 0o755)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
|
||||
def split_sections(s):
|
||||
|
||||
@@ -2,6 +2,8 @@ import os
|
||||
import errno
|
||||
import sys
|
||||
|
||||
from pipenv.patched.notpip._vendor import six
|
||||
|
||||
|
||||
def _makedirs_31(path, exist_ok=False):
|
||||
try:
|
||||
@@ -15,8 +17,7 @@ def _makedirs_31(path, exist_ok=False):
|
||||
# and exists_ok considerations are disentangled.
|
||||
# See https://github.com/pypa/setuptools/pull/1083#issuecomment-315168663
|
||||
needs_makedirs = (
|
||||
sys.version_info < (3, 2, 5) or
|
||||
(3, 3) <= sys.version_info < (3, 3, 6) or
|
||||
six.PY2 or
|
||||
(3, 4) <= sys.version_info < (3, 4, 1)
|
||||
)
|
||||
makedirs = _makedirs_31 if needs_makedirs else os.makedirs
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# module pyparsing.py
|
||||
#
|
||||
# Copyright (c) 2003-2016 Paul T. McGuire
|
||||
# Copyright (c) 2003-2018 Paul T. McGuire
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
@@ -25,6 +25,7 @@
|
||||
__doc__ = \
|
||||
"""
|
||||
pyparsing module - Classes and methods to define and execute parsing grammars
|
||||
=============================================================================
|
||||
|
||||
The pyparsing module is an alternative approach to creating and executing simple grammars,
|
||||
vs. the traditional lex/yacc approach, or the use of regular expressions. With pyparsing, you
|
||||
@@ -58,10 +59,23 @@ The pyparsing module handles some of the problems that are typically vexing when
|
||||
- extra or missing whitespace (the above program will also handle "Hello,World!", "Hello , World !", etc.)
|
||||
- quoted strings
|
||||
- embedded comments
|
||||
|
||||
|
||||
Getting Started -
|
||||
-----------------
|
||||
Visit the classes L{ParserElement} and L{ParseResults} to see the base classes that most other pyparsing
|
||||
classes inherit from. Use the docstrings for examples of how to:
|
||||
- construct literal match expressions from L{Literal} and L{CaselessLiteral} classes
|
||||
- construct character word-group expressions using the L{Word} class
|
||||
- see how to create repetitive expressions using L{ZeroOrMore} and L{OneOrMore} classes
|
||||
- use L{'+'<And>}, L{'|'<MatchFirst>}, L{'^'<Or>}, and L{'&'<Each>} operators to combine simple expressions into more complex ones
|
||||
- associate names with your parsed results using L{ParserElement.setResultsName}
|
||||
- find some helpful expression short-cuts like L{delimitedList} and L{oneOf}
|
||||
- find more useful common expressions in the L{pyparsing_common} namespace class
|
||||
"""
|
||||
|
||||
__version__ = "2.2.0"
|
||||
__versionTime__ = "06 Mar 2017 02:06 UTC"
|
||||
__version__ = "2.2.1"
|
||||
__versionTime__ = "18 Sep 2018 00:49 UTC"
|
||||
__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>"
|
||||
|
||||
import string
|
||||
@@ -82,6 +96,15 @@ try:
|
||||
except ImportError:
|
||||
from threading import RLock
|
||||
|
||||
try:
|
||||
# Python 3
|
||||
from collections.abc import Iterable
|
||||
from collections.abc import MutableMapping
|
||||
except ImportError:
|
||||
# Python 2.7
|
||||
from collections import Iterable
|
||||
from collections import MutableMapping
|
||||
|
||||
try:
|
||||
from collections import OrderedDict as _OrderedDict
|
||||
except ImportError:
|
||||
@@ -940,7 +963,7 @@ class ParseResults(object):
|
||||
def __dir__(self):
|
||||
return (dir(type(self)) + list(self.keys()))
|
||||
|
||||
collections.MutableMapping.register(ParseResults)
|
||||
MutableMapping.register(ParseResults)
|
||||
|
||||
def col (loc,strg):
|
||||
"""Returns current column within a string, counting newlines as line separators.
|
||||
@@ -1025,11 +1048,11 @@ def _trim_arity(func, maxargs=2):
|
||||
# special handling for Python 3.5.0 - extra deep call stack by 1
|
||||
offset = -3 if system_version == (3,5,0) else -2
|
||||
frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset]
|
||||
return [(frame_summary.filename, frame_summary.lineno)]
|
||||
return [frame_summary[:2]]
|
||||
def extract_tb(tb, limit=0):
|
||||
frames = traceback.extract_tb(tb, limit=limit)
|
||||
frame_summary = frames[-1]
|
||||
return [(frame_summary.filename, frame_summary.lineno)]
|
||||
return [frame_summary[:2]]
|
||||
else:
|
||||
extract_stack = traceback.extract_stack
|
||||
extract_tb = traceback.extract_tb
|
||||
@@ -1374,7 +1397,7 @@ class ParserElement(object):
|
||||
else:
|
||||
preloc = loc
|
||||
tokensStart = preloc
|
||||
if self.mayIndexError or loc >= len(instring):
|
||||
if self.mayIndexError or preloc >= len(instring):
|
||||
try:
|
||||
loc,tokens = self.parseImpl( instring, preloc, doActions )
|
||||
except IndexError:
|
||||
@@ -1408,7 +1431,6 @@ class ParserElement(object):
|
||||
self.resultsName,
|
||||
asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
|
||||
modal=self.modalResults )
|
||||
|
||||
if debugging:
|
||||
#~ print ("Matched",self,"->",retTokens.asList())
|
||||
if (self.debugActions[1] ):
|
||||
@@ -3242,7 +3264,7 @@ class ParseExpression(ParserElement):
|
||||
|
||||
if isinstance( exprs, basestring ):
|
||||
self.exprs = [ ParserElement._literalStringClass( exprs ) ]
|
||||
elif isinstance( exprs, collections.Iterable ):
|
||||
elif isinstance( exprs, Iterable ):
|
||||
exprs = list(exprs)
|
||||
# if sequence of strings provided, wrap with Literal
|
||||
if all(isinstance(expr, basestring) for expr in exprs):
|
||||
@@ -4393,7 +4415,7 @@ def traceParseAction(f):
|
||||
|
||||
@traceParseAction
|
||||
def remove_duplicate_chars(tokens):
|
||||
return ''.join(sorted(set(''.join(tokens)))
|
||||
return ''.join(sorted(set(''.join(tokens))))
|
||||
|
||||
wds = OneOrMore(wd).setParseAction(remove_duplicate_chars)
|
||||
print(wds.parseString("slkdjs sld sldd sdlf sdljf"))
|
||||
@@ -4583,7 +4605,7 @@ def oneOf( strs, caseless=False, useRegex=True ):
|
||||
symbols = []
|
||||
if isinstance(strs,basestring):
|
||||
symbols = strs.split()
|
||||
elif isinstance(strs, collections.Iterable):
|
||||
elif isinstance(strs, Iterable):
|
||||
symbols = list(strs)
|
||||
else:
|
||||
warnings.warn("Invalid argument to oneOf, expected string or iterable",
|
||||
@@ -4734,7 +4756,7 @@ stringEnd = StringEnd().setName("stringEnd")
|
||||
_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1])
|
||||
_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16)))
|
||||
_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8)))
|
||||
_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | Word(printables, excludeChars=r'\]', exact=1) | Regex(r"\w", re.UNICODE)
|
||||
_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | CharsNotIn(r'\]', exact=1)
|
||||
_charRange = Group(_singleChar + Suppress("-") + _singleChar)
|
||||
_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]"
|
||||
|
||||
|
||||
@@ -223,8 +223,8 @@ _float_re = re.compile(r'[+-]?(?:0|[1-9](?:_?\d)*)(?:\.\d(?:_?\d)*)?(?:[eE][+-]?
|
||||
_datetime_re = re.compile(r'(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:Z|([+-]\d{2}):(\d{2}))')
|
||||
|
||||
_basicstr_ml_re = re.compile(r'(?:(?:|"|"")[^"\\\000-\011\013-\037])*')
|
||||
_litstr_re = re.compile(r"[^'\000-\037]*")
|
||||
_litstr_ml_re = re.compile(r"(?:(?:|'|'')(?:[^'\000-\011\013-\037]))*")
|
||||
_litstr_re = re.compile(r"[^'\000\010\012-\037]*")
|
||||
_litstr_ml_re = re.compile(r"(?:(?:|'|'')(?:[^'\000-\010\013-\037]))*")
|
||||
def _p_value(s, object_pairs_hook):
|
||||
pos = s.pos()
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ except (AssertionError, ValueError):
|
||||
RequestsDependencyWarning)
|
||||
|
||||
# Attempt to enable urllib3's SNI support, if possible
|
||||
from pipenv.patched.notpip._internal.compat import WINDOWS
|
||||
from pipenv.patched.notpip._internal.utils.compat import WINDOWS
|
||||
if not WINDOWS:
|
||||
try:
|
||||
from pipenv.patched.notpip._vendor.urllib3.contrib import pyopenssl
|
||||
|
||||
@@ -9,14 +9,15 @@ msgpack-python==0.5.6
|
||||
lockfile==0.12.2
|
||||
progress==1.4
|
||||
ipaddress==1.0.22 # Only needed on 2.6 and 2.7
|
||||
packaging==17.1
|
||||
pyparsing==2.2.0
|
||||
pytoml==0.1.16
|
||||
packaging==18.0
|
||||
pep517==0.2
|
||||
pyparsing==2.2.1
|
||||
pytoml==0.1.19
|
||||
retrying==1.3.3
|
||||
requests==2.19.1
|
||||
chardet==3.0.4
|
||||
idna==2.7
|
||||
urllib3==1.23
|
||||
certifi==2018.4.16
|
||||
setuptools==39.2.0
|
||||
certifi==2018.8.24
|
||||
setuptools==40.4.3
|
||||
webencodings==0.5.1
|
||||
|
||||
@@ -4,4 +4,4 @@ crayons==0.1.2
|
||||
pipfile==0.0.2
|
||||
pip-tools==3.1.0
|
||||
prettytoml==0.3
|
||||
pip==18.0
|
||||
pip==18.1
|
||||
|
||||
@@ -1,49 +1,55 @@
|
||||
# -*- coding=utf-8 -*-
|
||||
import importlib
|
||||
|
||||
from pip_shims import pip_version
|
||||
import pkg_resources
|
||||
__all__ = [
|
||||
"InstallRequirement",
|
||||
"parse_requirements",
|
||||
"RequirementSet",
|
||||
"user_cache_dir",
|
||||
"FAVORITE_HASH",
|
||||
"is_file_url",
|
||||
"url_to_path",
|
||||
"PackageFinder",
|
||||
"FormatControl",
|
||||
"Wheel",
|
||||
"Command",
|
||||
"cmdoptions",
|
||||
"get_installed_distributions",
|
||||
"PyPI",
|
||||
"SafeFileCache",
|
||||
"InstallationError",
|
||||
"parse_version",
|
||||
"pip_version",
|
||||
"install_req_from_editable",
|
||||
"install_req_from_line",
|
||||
"user_cache_dir"
|
||||
]
|
||||
|
||||
def do_import(module_path, subimport=None, old_path=None, vendored_name=None):
|
||||
old_path = old_path or module_path
|
||||
prefix = vendored_name if vendored_name else "pip"
|
||||
prefixes = ["{0}._internal".format(prefix), "{0}".format(prefix)]
|
||||
paths = [module_path, old_path]
|
||||
search_order = ["{0}.{1}".format(p, pth) for p in prefixes for pth in paths if pth is not None]
|
||||
package = subimport if subimport else None
|
||||
for to_import in search_order:
|
||||
if not subimport:
|
||||
to_import, _, package = to_import.rpartition(".")
|
||||
try:
|
||||
imported = importlib.import_module(to_import)
|
||||
except ImportError:
|
||||
continue
|
||||
else:
|
||||
return getattr(imported, package)
|
||||
|
||||
|
||||
InstallRequirement = do_import('req.req_install', 'InstallRequirement', vendored_name="notpip")
|
||||
parse_requirements = do_import('req.req_file', 'parse_requirements', vendored_name="notpip")
|
||||
RequirementSet = do_import('req.req_set', 'RequirementSet', vendored_name="notpip")
|
||||
user_cache_dir = do_import('utils.appdirs', 'user_cache_dir', vendored_name="notpip")
|
||||
FAVORITE_HASH = do_import('utils.hashes', 'FAVORITE_HASH', vendored_name="notpip")
|
||||
is_file_url = do_import('download', 'is_file_url', vendored_name="notpip")
|
||||
url_to_path = do_import('download', 'url_to_path', vendored_name="notpip")
|
||||
PackageFinder = do_import('index', 'PackageFinder', vendored_name="notpip")
|
||||
FormatControl = do_import('index', 'FormatControl', vendored_name="notpip")
|
||||
Wheel = do_import('wheel', 'Wheel', vendored_name="notpip")
|
||||
Command = do_import('cli.base_command', 'Command', old_path='basecommand', vendored_name="notpip")
|
||||
cmdoptions = do_import('cli.cmdoptions', old_path='cmdoptions', vendored_name="notpip")
|
||||
get_installed_distributions = do_import('utils.misc', 'get_installed_distributions', old_path='utils', vendored_name="notpip")
|
||||
PyPI = do_import('models.index', 'PyPI', vendored_name='notpip')
|
||||
SafeFileCache = do_import('download', 'SafeFileCache', vendored_name='notpip')
|
||||
InstallationError = do_import('exceptions', 'InstallationError', vendored_name='notpip')
|
||||
from pipenv.vendor.appdirs import user_cache_dir
|
||||
from pip_shims.shims import (
|
||||
InstallRequirement,
|
||||
parse_requirements,
|
||||
RequirementSet,
|
||||
FAVORITE_HASH,
|
||||
is_file_url,
|
||||
url_to_path,
|
||||
PackageFinder,
|
||||
FormatControl,
|
||||
Wheel,
|
||||
Command,
|
||||
cmdoptions,
|
||||
get_installed_distributions,
|
||||
PyPI,
|
||||
SafeFileCache,
|
||||
InstallationError,
|
||||
parse_version,
|
||||
pip_version,
|
||||
)
|
||||
|
||||
# pip 18.1 has refactored InstallRequirement constructors use by pip-tools.
|
||||
if pkg_resources.parse_version(pip_version) < pkg_resources.parse_version('18.1'):
|
||||
if parse_version(pip_version) < parse_version('18.1'):
|
||||
install_req_from_line = InstallRequirement.from_line
|
||||
install_req_from_editable = InstallRequirement.from_editable
|
||||
else:
|
||||
install_req_from_line = do_import('req.constructors', 'install_req_from_line', vendored_name="notpip")
|
||||
install_req_from_editable = do_import('req.constructors', 'install_req_from_editable', vendored_name="notpip")
|
||||
|
||||
from pip_shims.shims import (
|
||||
install_req_from_editable, install_req_from_line
|
||||
)
|
||||
|
||||
+3
-2
@@ -639,8 +639,9 @@ class Project(object):
|
||||
|
||||
def create_pipfile(self, python=None):
|
||||
"""Creates the Pipfile, filled with juicy defaults."""
|
||||
from .patched.notpip._internal import ConfigOptionParser
|
||||
from .patched.notpip._internal.cmdoptions import make_option_group, index_group
|
||||
from .vendor.pip_shims.shims import (
|
||||
ConfigOptionParser, make_option_group, index_group
|
||||
)
|
||||
|
||||
config_parser = ConfigOptionParser(name=self.name)
|
||||
config_parser.add_option_group(make_option_group(index_group, config_parser))
|
||||
|
||||
+11
-2
@@ -97,9 +97,16 @@ def main():
|
||||
import warnings
|
||||
from pipenv.vendor.vistir.compat import ResourceWarning
|
||||
warnings.simplefilter("ignore", category=ResourceWarning)
|
||||
from pipenv.vendor import colorama
|
||||
colorama.init()
|
||||
import io
|
||||
import six
|
||||
if six.PY3:
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf8')
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer,encoding='utf8')
|
||||
else:
|
||||
from pipenv._compat import force_encoding
|
||||
force_encoding()
|
||||
os.environ["PIP_DISABLE_PIP_VERSION_CHECK"] = str("1")
|
||||
os.environ["PYTHONIOENCODING"] = str("utf-8")
|
||||
parser = get_parser()
|
||||
parsed, remaining = parser.parse_known_args()
|
||||
# sys.argv = remaining
|
||||
@@ -110,4 +117,6 @@ def main():
|
||||
|
||||
if __name__ == "__main__":
|
||||
_patch_path()
|
||||
from pipenv.vendor import colorama
|
||||
colorama.init()
|
||||
main()
|
||||
|
||||
+17
-17
@@ -211,10 +211,10 @@ def actually_resolve_deps(
|
||||
pre,
|
||||
req_dir=None,
|
||||
):
|
||||
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 .vendor.pip_shims.shims import (
|
||||
Command, parse_requirements, DistributionNotFound
|
||||
)
|
||||
from .vendor.requests.exceptions import HTTPError
|
||||
from pipenv.patched.piptools.resolver import Resolver
|
||||
from pipenv.patched.piptools.repositories.pypi import PyPIRepository
|
||||
from pipenv.patched.piptools.scripts.compile import get_pip_command
|
||||
@@ -223,7 +223,7 @@ def actually_resolve_deps(
|
||||
from .vendor.requirementslib.models.requirements import Requirement
|
||||
from .vendor.vistir.path import create_tracked_tempdir, create_tracked_tempfile
|
||||
|
||||
class PipCommand(basecommand.Command):
|
||||
class PipCommand(Command):
|
||||
"""Needed for pip-tools."""
|
||||
|
||||
name = "PipCommand"
|
||||
@@ -338,6 +338,7 @@ def venv_resolve_deps(
|
||||
from .vendor.pexpect.exceptions import EOF, TIMEOUT
|
||||
from .vendor import delegator
|
||||
from . import resolver
|
||||
from ._compat import decode_output
|
||||
import json
|
||||
|
||||
if not deps:
|
||||
@@ -380,14 +381,14 @@ def venv_resolve_deps(
|
||||
break
|
||||
_out = c.subprocess.before
|
||||
if _out is not None:
|
||||
_out = to_native_string("{0}".format(_out))
|
||||
_out = decode_output("{0}".format(_out))
|
||||
out += _out
|
||||
sp.text = to_native_string("Locking... {0}".format(_out[:100]))
|
||||
if environments.is_verbose():
|
||||
if _out is not None:
|
||||
sp._hide_cursor()
|
||||
sp.write(_out.rstrip())
|
||||
sp._show_cursor()
|
||||
sp.text = to_native_string("{0}".format(_out[:100]))
|
||||
if environments.is_verbose():
|
||||
if _out is not None:
|
||||
sp._hide_cursor()
|
||||
sp.write(_out.rstrip())
|
||||
sp._show_cursor()
|
||||
c.block()
|
||||
if c.return_code != 0:
|
||||
sp.red.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format(
|
||||
@@ -423,7 +424,7 @@ def resolve_deps(
|
||||
"""Given a list of dependencies, return a resolved list of dependencies,
|
||||
using pip-tools -- and their hashes, using the warehouse API / pip.
|
||||
"""
|
||||
from .patched.notpip._vendor.requests.exceptions import ConnectionError
|
||||
from .vendor.requests.exceptions import ConnectionError
|
||||
from .vendor.requirementslib.models.requirements import Requirement
|
||||
|
||||
index_lookup = {}
|
||||
@@ -637,9 +638,8 @@ def is_editable(pipfile_entry):
|
||||
|
||||
def is_installable_file(path):
|
||||
"""Determine if a path can potentially be installed"""
|
||||
from .patched.notpip._internal.utils.misc import is_installable_dir
|
||||
from .vendor.pip_shims.shims import is_installable_dir, is_archive_file
|
||||
from .patched.notpip._internal.utils.packaging import specifiers
|
||||
from .patched.notpip._internal.download import is_archive_file
|
||||
from ._compat import Path
|
||||
|
||||
if hasattr(path, "keys") and any(
|
||||
@@ -691,7 +691,7 @@ def is_file(package):
|
||||
|
||||
def pep440_version(version):
|
||||
"""Normalize version to PEP 440 standards"""
|
||||
from .patched.notpip._internal.index import parse_version
|
||||
from .vendor.pip_shims.shims import parse_version
|
||||
|
||||
# Use pip built-in version parser.
|
||||
return str(parse_version(version))
|
||||
@@ -1149,7 +1149,7 @@ def translate_markers(pipfile_entry):
|
||||
"""
|
||||
if not isinstance(pipfile_entry, Mapping):
|
||||
raise TypeError("Entry is not a pipfile formatted mapping.")
|
||||
from notpip._vendor.distlib.markers import DEFAULT_CONTEXT as marker_context
|
||||
from .vendor.distlib.markers import DEFAULT_CONTEXT as marker_context
|
||||
from .vendor.packaging.markers import Marker
|
||||
from .vendor.vistir.misc import dedup
|
||||
|
||||
|
||||
Vendored
+2
-4
@@ -1,7 +1,5 @@
|
||||
# See https://pypi.python.org/pypi/backports
|
||||
|
||||
from pkgutil import extend_path
|
||||
__path__ = extend_path(__path__, __name__)
|
||||
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
|
||||
from . import weakref
|
||||
from . import enum
|
||||
from . import shutil_get_terminal_size
|
||||
from . import functools_lru_cache
|
||||
|
||||
Vendored
+7
@@ -95,6 +95,13 @@ class DotEnv():
|
||||
for k, v in self.dict().items():
|
||||
if k in os.environ and not override:
|
||||
continue
|
||||
# With Python 2 on Windows, ensuree environment variables are
|
||||
# system strings to avoid "TypeError: environment can only contain
|
||||
# strings" in Python's subprocess module.
|
||||
if sys.version_info.major < 3 and sys.platform == 'win32':
|
||||
from pipenv.utils import fs_str
|
||||
k = fs_str(k)
|
||||
v = fs_str(v)
|
||||
os.environ[k] = v
|
||||
|
||||
return True
|
||||
|
||||
Vendored
-29
@@ -1,29 +0,0 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018, Brett Cannon
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+1
-1
@@ -216,7 +216,7 @@ def _read_requires_python(metadata):
|
||||
|
||||
|
||||
def _get_dependencies_from_pip(ireq, sources):
|
||||
"""Retrieves dependencies for the requirement from pip internals.
|
||||
"""Retrieves dependencies for the requirement from pipenv.patched.notpip internals.
|
||||
|
||||
The current strategy is to try the followings in order, returning the
|
||||
first successful result.
|
||||
|
||||
Vendored
+1
-1
@@ -3,7 +3,7 @@ from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
|
||||
__version__ = '0.3.1'
|
||||
__version__ = '0.3.2'
|
||||
|
||||
from . import shims
|
||||
|
||||
|
||||
Vendored
+4
@@ -43,6 +43,9 @@ class _shims(object):
|
||||
"pip_shims.utils": utils
|
||||
}
|
||||
self.pip_version = getattr(self._modules["pip"], "__version__")
|
||||
version_types = ["post", "pre", "dev", "rc"]
|
||||
if any(post in self.pip_version.rsplit(".")[-1] for post in version_types):
|
||||
self.pip_version, _, _ = self.pip_version.rpartition(".")
|
||||
self.parsed_pip_version = self._parse(self.pip_version)
|
||||
self._contextmanagers = ("RequirementTracker",)
|
||||
self._moves = {
|
||||
@@ -140,6 +143,7 @@ class _shims(object):
|
||||
("wheel.WheelCache", "7", "9.0.3")
|
||||
),
|
||||
"WheelBuilder": ("wheel.WheelBuilder", "7.0.0", "9999"),
|
||||
"PyPI": ("models.index.PyPI", "7.0.0", "9999"),
|
||||
}
|
||||
|
||||
def _ensure_methods(self, cls, classname, *methods):
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
from __future__ import print_function, absolute_import
|
||||
|
||||
__version__ = '1.1.5'
|
||||
__version__ = '1.1.6'
|
||||
|
||||
# Add NullHandler to "pythonfinder" logger, because Python2's default root
|
||||
# logger has no handler and warnings like this would be reported:
|
||||
|
||||
+1
@@ -27,6 +27,7 @@ from ..utils import (
|
||||
path_is_known_executable,
|
||||
unnest,
|
||||
)
|
||||
from .python import PythonVersion
|
||||
|
||||
|
||||
@attr.s
|
||||
|
||||
+2
@@ -14,6 +14,8 @@ from vistir.compat import Path
|
||||
from ..utils import (
|
||||
ensure_path,
|
||||
optional_instance_of,
|
||||
get_python_version,
|
||||
filter_pythons,
|
||||
unnest,
|
||||
)
|
||||
from .mixins import BaseFinder, BasePath
|
||||
|
||||
Vendored
+1
-1
@@ -4,7 +4,7 @@ Copyright 2018 Kenneth Reitz
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+1
-1
@@ -132,7 +132,7 @@ def strip_ssh_from_git_uri(uri):
|
||||
|
||||
|
||||
def add_ssh_scheme_to_git_uri(uri):
|
||||
"""Cleans VCS uris from pip format"""
|
||||
"""Cleans VCS uris from pipenv.patched.notpip format"""
|
||||
if isinstance(uri, six.string_types):
|
||||
# Add scheme for parsing purposes, this is also what pip does
|
||||
if uri.startswith("git+") and "://" not in uri:
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
__title__ = "shutil_backports"
|
||||
__version__ = "0.1.0"
|
||||
__license__ = "MIT"
|
||||
__author__ = "Christopher Rosell"
|
||||
__copyright__ = "Copyright 2014 Christopher Rosell"
|
||||
|
||||
__all__ = ["get_terminal_size"]
|
||||
|
||||
from .get_terminal_size import *
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user