470 lines
15 KiB
Python
470 lines
15 KiB
Python
# -*- coding=utf-8 -*-
|
|
from __future__ import absolute_import
|
|
|
|
import os
|
|
|
|
import click.types
|
|
|
|
from click import (
|
|
BadParameter, BadArgumentUsage, Group, Option, argument, echo, make_pass_decorator, option
|
|
)
|
|
from click_didyoumean import DYMMixin
|
|
|
|
from .. import environments
|
|
from ..utils import is_valid_url
|
|
|
|
|
|
CONTEXT_SETTINGS = {
|
|
"help_option_names": ["-h", "--help"],
|
|
"auto_envvar_prefix": "PIPENV"
|
|
}
|
|
|
|
|
|
class PipenvGroup(DYMMixin, Group):
|
|
"""Custom Group class provides formatted main help"""
|
|
|
|
def get_help_option(self, ctx):
|
|
from ..core import format_help
|
|
|
|
"""Override for showing formatted main help via --help and -h options"""
|
|
help_options = self.get_help_option_names(ctx)
|
|
if not help_options or not self.add_help_option:
|
|
return
|
|
|
|
def show_help(ctx, param, value):
|
|
if value and not ctx.resilient_parsing:
|
|
if not ctx.invoked_subcommand:
|
|
# legit main help
|
|
echo(format_help(ctx.get_help()))
|
|
else:
|
|
# legit sub-command help
|
|
echo(ctx.get_help(), color=ctx.color)
|
|
ctx.exit()
|
|
|
|
return Option(
|
|
help_options,
|
|
is_flag=True,
|
|
is_eager=True,
|
|
expose_value=False,
|
|
callback=show_help,
|
|
help="Show this message and exit.",
|
|
)
|
|
|
|
|
|
class State(object):
|
|
def __init__(self):
|
|
self.index = None
|
|
self.extra_index_urls = []
|
|
self.verbose = False
|
|
self.quiet = False
|
|
self.pypi_mirror = None
|
|
self.python = None
|
|
self.two = None
|
|
self.three = None
|
|
self.site_packages = None
|
|
self.clear = False
|
|
self.system = False
|
|
self.installstate = InstallState()
|
|
self.lockoptions = LockOptions()
|
|
|
|
|
|
class InstallState(object):
|
|
def __init__(self):
|
|
self.dev = False
|
|
self.pre = False
|
|
self.selective_upgrade = False
|
|
self.keep_outdated = False
|
|
self.skip_lock = False
|
|
self.ignore_pipfile = False
|
|
self.sequential = False
|
|
self.code = False
|
|
self.requirementstxt = None
|
|
self.deploy = False
|
|
self.packages = []
|
|
self.editables = []
|
|
|
|
|
|
class LockOptions(object):
|
|
def __init__(self):
|
|
self.dev_only = False
|
|
self.emit_requirements = False
|
|
self.emit_requirements_header = False
|
|
|
|
|
|
pass_state = make_pass_decorator(State, ensure=True)
|
|
|
|
|
|
def index_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.index = value
|
|
return value
|
|
return option('-i', '--index', expose_value=False, envvar="PIP_INDEX_URL",
|
|
help='Target PyPI-compatible package index url.', nargs=1,
|
|
callback=callback)(f)
|
|
|
|
|
|
def extra_index_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.extra_index_urls.extend(list(value))
|
|
return value
|
|
return option("--extra-index-url", multiple=True, expose_value=False,
|
|
help=u"URLs to the extra PyPI compatible indexes to query for package look-ups.",
|
|
callback=callback, envvar="PIP_EXTRA_INDEX_URL")(f)
|
|
|
|
|
|
def editable_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.installstate.editables.extend(value)
|
|
return value
|
|
return option('-e', '--editable', expose_value=False, multiple=True,
|
|
callback=callback, type=click.types.STRING, help=(
|
|
"An editable Python package URL or path, often to a VCS "
|
|
"repository."
|
|
))(f)
|
|
|
|
|
|
def sequential_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.installstate.sequential = value
|
|
return value
|
|
return option("--sequential", is_flag=True, default=False, expose_value=False,
|
|
help="Install dependencies one-at-a-time, instead of concurrently.",
|
|
callback=callback, type=click.types.BOOL, show_envvar=True)(f)
|
|
|
|
|
|
def skip_lock_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.installstate.skip_lock = value
|
|
return value
|
|
return option("--skip-lock", is_flag=True, default=False, expose_value=False,
|
|
help=u"Skip locking mechanisms and use the Pipfile instead during operation.",
|
|
envvar="PIPENV_SKIP_LOCK", callback=callback, type=click.types.BOOL,
|
|
show_envvar=True)(f)
|
|
|
|
|
|
def keep_outdated_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.installstate.keep_outdated = value
|
|
return value
|
|
return option("--keep-outdated", is_flag=True, default=False, expose_value=False,
|
|
help=u"Keep out-dated dependencies from being updated in Pipfile.lock.",
|
|
callback=callback, type=click.types.BOOL, show_envvar=True)(f)
|
|
|
|
|
|
def selective_upgrade_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.installstate.selective_upgrade = value
|
|
return value
|
|
return option("--selective-upgrade", is_flag=True, default=False, type=click.types.BOOL,
|
|
help="Update specified packages.", callback=callback,
|
|
expose_value=False)(f)
|
|
|
|
|
|
def ignore_pipfile_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.installstate.ignore_pipfile = value
|
|
return value
|
|
return option("--ignore-pipfile", is_flag=True, default=False, expose_value=False,
|
|
help="Ignore Pipfile when installing, using the Pipfile.lock.",
|
|
callback=callback, type=click.types.BOOL, show_envvar=True)(f)
|
|
|
|
|
|
def _dev_option(f, help_text):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.installstate.dev = value
|
|
return value
|
|
return option("--dev", "-d", is_flag=True, default=False, type=click.types.BOOL,
|
|
help=help_text, callback=callback,
|
|
expose_value=False, show_envvar=True)(f)
|
|
|
|
|
|
def install_dev_option(f):
|
|
return _dev_option(f, "Install both develop and default packages")
|
|
|
|
|
|
def lock_dev_option(f):
|
|
return _dev_option(f, "Generate both develop and default requirements")
|
|
|
|
|
|
def uninstall_dev_option(f):
|
|
return _dev_option(f, "Deprecated (as it has no effect). May be removed in a future release.")
|
|
|
|
|
|
def pre_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.installstate.pre = value
|
|
return value
|
|
return option("--pre", is_flag=True, default=False, help=u"Allow pre-releases.",
|
|
callback=callback, type=click.types.BOOL, expose_value=False)(f)
|
|
|
|
|
|
def package_arg(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.installstate.packages.extend(value)
|
|
return value
|
|
return argument('packages', nargs=-1, callback=callback, expose_value=False,
|
|
type=click.types.STRING)(f)
|
|
|
|
|
|
def three_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
if value is not None:
|
|
state.three = value
|
|
state.two = not value
|
|
return value
|
|
return option("--three/--two", is_flag=True, default=None,
|
|
help="Use Python 3/2 when creating virtualenv.", callback=callback,
|
|
expose_value=False)(f)
|
|
|
|
|
|
def python_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
if value is not None:
|
|
state.python = validate_python_path(ctx, param, value)
|
|
return value
|
|
return option("--python", default=False, nargs=1, callback=callback,
|
|
help="Specify which version of Python virtualenv should use.",
|
|
expose_value=False, allow_from_autoenv=False)(f)
|
|
|
|
|
|
def pypi_mirror_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
if value is not None:
|
|
state.pypi_mirror = validate_pypi_mirror(ctx, param, value)
|
|
return value
|
|
return option("--pypi-mirror", default=environments.PIPENV_PYPI_MIRROR, nargs=1,
|
|
callback=callback, help="Specify a PyPI mirror.", expose_value=False)(f)
|
|
|
|
|
|
def verbose_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
if value:
|
|
if state.quiet:
|
|
raise BadArgumentUsage(
|
|
"--verbose and --quiet are mutually exclusive! Please choose one!",
|
|
ctx=ctx
|
|
)
|
|
state.verbose = True
|
|
setup_verbosity(ctx, param, 1)
|
|
return option("--verbose", "-v", is_flag=True, expose_value=False,
|
|
callback=callback, help="Verbose mode.", type=click.types.BOOL)(f)
|
|
|
|
|
|
def quiet_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
if value:
|
|
if state.verbose:
|
|
raise BadArgumentUsage(
|
|
"--verbose and --quiet are mutually exclusive! Please choose one!",
|
|
ctx=ctx
|
|
)
|
|
state.quiet = True
|
|
setup_verbosity(ctx, param, -1)
|
|
return option("--quiet", "-q", is_flag=True, expose_value=False,
|
|
callback=callback, help="Quiet mode.", type=click.types.BOOL)(f)
|
|
|
|
|
|
def site_packages_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
validate_bool_or_none(ctx, param, value)
|
|
state.site_packages = value
|
|
return value
|
|
return option("--site-packages/--no-site-packages", is_flag=True, default=None,
|
|
help="Enable site-packages for the virtualenv.", callback=callback,
|
|
expose_value=False, show_envvar=True)(f)
|
|
|
|
|
|
def clear_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.clear = value
|
|
return value
|
|
return option("--clear", is_flag=True, callback=callback, type=click.types.BOOL,
|
|
help="Clears caches (pipenv, pip, and pip-tools).",
|
|
expose_value=False, show_envvar=True)(f)
|
|
|
|
|
|
def system_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
if value is not None:
|
|
state.system = value
|
|
return value
|
|
return option("--system", is_flag=True, default=False, help="System pip management.",
|
|
callback=callback, type=click.types.BOOL, expose_value=False,
|
|
show_envvar=True)(f)
|
|
|
|
|
|
def requirementstxt_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
if value:
|
|
state.installstate.requirementstxt = value
|
|
return value
|
|
return option("--requirements", "-r", nargs=1, default=False, expose_value=False,
|
|
help="Import a requirements.txt file.", callback=callback)(f)
|
|
|
|
|
|
def emit_requirements_flag(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
if value:
|
|
state.lockoptions.emit_requirements = value
|
|
return value
|
|
return option("--requirements", "-r", default=False, is_flag=True, expose_value=False,
|
|
help="Generate output in requirements.txt format.", callback=callback)(f)
|
|
|
|
|
|
def emit_requirements_header_flag(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
if value:
|
|
state.lockoptions.emit_requirements_header = value
|
|
return value
|
|
return option("--header/--no-header", default=True, is_flag=True, expose_value=False,
|
|
help="Add header to generated requirements", callback=callback)(f)
|
|
|
|
|
|
def dev_only_flag(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
if value:
|
|
state.lockoptions.dev_only = value
|
|
return value
|
|
return option("--dev-only", default=False, is_flag=True, expose_value=False,
|
|
help="Emit development dependencies *only* (overrides --dev)", callback=callback)(f)
|
|
|
|
|
|
def code_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
if value:
|
|
state.installstate.code = value
|
|
return value
|
|
return option("--code", "-c", nargs=1, default=False, help="Install packages "
|
|
"automatically discovered from import statements.", callback=callback,
|
|
expose_value=False)(f)
|
|
|
|
|
|
def deploy_option(f):
|
|
def callback(ctx, param, value):
|
|
state = ctx.ensure_object(State)
|
|
state.installstate.deploy = value
|
|
return value
|
|
return option("--deploy", is_flag=True, default=False, type=click.types.BOOL,
|
|
help=u"Abort if the Pipfile.lock is out-of-date, or Python version is"
|
|
" wrong.", callback=callback, expose_value=False)(f)
|
|
|
|
|
|
def setup_verbosity(ctx, param, value):
|
|
if not value:
|
|
return
|
|
import logging
|
|
loggers = ("pip", "piptools")
|
|
if value == 1:
|
|
for logger in loggers:
|
|
logging.getLogger(logger).setLevel(logging.INFO)
|
|
elif value == -1:
|
|
for logger in loggers:
|
|
logging.getLogger(logger).setLevel(logging.CRITICAL)
|
|
environments.PIPENV_VERBOSITY = value
|
|
|
|
|
|
def validate_python_path(ctx, param, value):
|
|
# Validating the Python path is complicated by accepting a number of
|
|
# friendly options: the default will be boolean False to enable
|
|
# autodetection but it may also be a value which will be searched in
|
|
# the path or an absolute path. To report errors as early as possible
|
|
# we'll report absolute paths which do not exist:
|
|
if isinstance(value, (str, bytes)):
|
|
if os.path.isabs(value) and not os.path.isfile(value):
|
|
raise BadParameter("Expected Python at path %s does not exist" % value)
|
|
return value
|
|
|
|
|
|
def validate_bool_or_none(ctx, param, value):
|
|
if value is not None:
|
|
return click.types.BOOL(value)
|
|
return False
|
|
|
|
|
|
def validate_pypi_mirror(ctx, param, value):
|
|
if value and not is_valid_url(value):
|
|
raise BadParameter("Invalid PyPI mirror URL: %s" % value)
|
|
return value
|
|
|
|
|
|
def common_options(f):
|
|
f = pypi_mirror_option(f)
|
|
f = verbose_option(f)
|
|
f = clear_option(f)
|
|
f = three_option(f)
|
|
f = python_option(f)
|
|
return f
|
|
|
|
|
|
def install_base_options(f):
|
|
f = common_options(f)
|
|
f = pre_option(f)
|
|
f = keep_outdated_option(f)
|
|
return f
|
|
|
|
|
|
def uninstall_options(f):
|
|
f = install_base_options(f)
|
|
f = uninstall_dev_option(f)
|
|
f = skip_lock_option(f)
|
|
f = editable_option(f)
|
|
f = package_arg(f)
|
|
return f
|
|
|
|
|
|
def lock_options(f):
|
|
f = install_base_options(f)
|
|
f = lock_dev_option(f)
|
|
f = emit_requirements_flag(f)
|
|
f = dev_only_flag(f)
|
|
return f
|
|
|
|
|
|
def sync_options(f):
|
|
f = install_base_options(f)
|
|
f = install_dev_option(f)
|
|
f = sequential_option(f)
|
|
return f
|
|
|
|
|
|
def install_options(f):
|
|
f = sync_options(f)
|
|
f = index_option(f)
|
|
f = extra_index_option(f)
|
|
f = requirementstxt_option(f)
|
|
f = selective_upgrade_option(f)
|
|
f = ignore_pipfile_option(f)
|
|
f = editable_option(f)
|
|
f = package_arg(f)
|
|
return f
|
|
|
|
|
|
def general_options(f):
|
|
f = common_options(f)
|
|
f = site_packages_option(f)
|
|
return f
|