mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
62c2bd78ca
`if no packages are given` sounds a bit better than `if none is given` when referring to `provided packages`. Also mention that `pipenv install` with no provided packages installs all packages from Pipfile. It is not obvious for people new to pipenv whether pipenv is using Pipfile or Pipfile.lock by default.
610 lines
18 KiB
Python
610 lines
18 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import absolute_import
|
|
|
|
import os
|
|
import sys
|
|
|
|
import crayons
|
|
import delegator
|
|
|
|
from click import (
|
|
argument, echo, edit, group, option, pass_context, secho, version_option
|
|
)
|
|
|
|
import click_completion
|
|
|
|
from click_didyoumean import DYMCommandCollection
|
|
|
|
from .. import environments
|
|
from ..__version__ import __version__
|
|
from .options import (
|
|
CONTEXT_SETTINGS, PipenvGroup, code_option, common_options, deploy_option,
|
|
general_options, install_options, lock_options, pass_state, skip_lock_option,
|
|
pypi_mirror_option, python_option, requirementstxt_option, sync_options,
|
|
system_option, three_option, verbose_option, uninstall_options
|
|
)
|
|
|
|
|
|
# Enable shell completion.
|
|
click_completion.init()
|
|
|
|
|
|
@group(cls=PipenvGroup, invoke_without_command=True, context_settings=CONTEXT_SETTINGS)
|
|
@option("--where", is_flag=True, default=False, help="Output project home information.")
|
|
@option("--venv", is_flag=True, default=False, help="Output virtualenv information.")
|
|
@option(
|
|
"--py", is_flag=True, default=False, help="Output Python interpreter information."
|
|
)
|
|
@option(
|
|
"--envs", is_flag=True, default=False, help="Output Environment Variable options."
|
|
)
|
|
@option("--rm", is_flag=True, default=False, help="Remove the virtualenv.")
|
|
@option("--bare", is_flag=True, default=False, help="Minimal output.")
|
|
@option(
|
|
"--completion",
|
|
is_flag=True,
|
|
default=False,
|
|
help="Output completion (to be eval'd).",
|
|
)
|
|
@option("--man", is_flag=True, default=False, help="Display manpage.")
|
|
@option(
|
|
"--support",
|
|
is_flag=True,
|
|
help="Output diagnostic information for use in GitHub issues.",
|
|
)
|
|
@general_options
|
|
@version_option(prog_name=crayons.normal("pipenv", bold=True), version=__version__)
|
|
@pass_state
|
|
@pass_context
|
|
def cli(
|
|
ctx,
|
|
state,
|
|
where=False,
|
|
venv=False,
|
|
rm=False,
|
|
bare=False,
|
|
three=False,
|
|
python=False,
|
|
help=False,
|
|
py=False,
|
|
site_packages=False,
|
|
envs=False,
|
|
man=False,
|
|
completion=False,
|
|
pypi_mirror=None,
|
|
support=None,
|
|
clear=False,
|
|
**kwargs
|
|
):
|
|
# Handle this ASAP to make shell startup fast.
|
|
if completion:
|
|
from .. import shells
|
|
|
|
try:
|
|
shell = shells.detect_info()[0]
|
|
except shells.ShellDetectionFailure:
|
|
echo(
|
|
"Fail to detect shell. Please provide the {0} environment "
|
|
"variable.".format(crayons.normal("PIPENV_SHELL", bold=True)),
|
|
err=True,
|
|
)
|
|
ctx.abort()
|
|
print(click_completion.get_code(shell=shell, prog_name="pipenv"))
|
|
return 0
|
|
|
|
from ..core import (
|
|
system_which,
|
|
do_py,
|
|
warn_in_virtualenv,
|
|
do_where,
|
|
project,
|
|
spinner,
|
|
cleanup_virtualenv,
|
|
ensure_project,
|
|
format_help,
|
|
do_clear,
|
|
)
|
|
|
|
if man:
|
|
if system_which("man"):
|
|
path = os.sep.join([os.path.dirname(__file__), "pipenv.1"])
|
|
os.execle(system_which("man"), "man", path, os.environ)
|
|
return 0
|
|
else:
|
|
secho("man does not appear to be available on your system.", fg="yellow", bold=True, err=True)
|
|
return 1
|
|
if envs:
|
|
echo("The following environment variables can be set, to do various things:\n")
|
|
for key in environments.__dict__:
|
|
if key.startswith("PIPENV"):
|
|
echo(" - {0}".format(crayons.normal(key, bold=True)))
|
|
echo(
|
|
"\nYou can learn more at:\n {0}".format(
|
|
crayons.green(
|
|
"http://docs.pipenv.org/advanced/#configuration-with-environment-variables"
|
|
)
|
|
)
|
|
)
|
|
return 0
|
|
warn_in_virtualenv()
|
|
if ctx.invoked_subcommand is None:
|
|
# --where was passed…
|
|
if where:
|
|
do_where(bare=True)
|
|
return 0
|
|
elif py:
|
|
do_py()
|
|
return 0
|
|
# --support was passed…
|
|
elif support:
|
|
from ..help import get_pipenv_diagnostics
|
|
|
|
get_pipenv_diagnostics()
|
|
return 0
|
|
# --clear was passed…
|
|
elif clear:
|
|
do_clear()
|
|
return 0
|
|
|
|
# --venv was passed…
|
|
elif venv:
|
|
# There is no virtualenv yet.
|
|
if not project.virtualenv_exists:
|
|
echo(
|
|
crayons.red("No virtualenv has been created for this project yet!"),
|
|
err=True,
|
|
)
|
|
ctx.abort()
|
|
else:
|
|
echo(project.virtualenv_location)
|
|
return 0
|
|
# --rm was passed…
|
|
elif rm:
|
|
# Abort if --system (or running in a virtualenv).
|
|
if environments.PIPENV_USE_SYSTEM:
|
|
echo(
|
|
crayons.red(
|
|
"You are attempting to remove a virtualenv that "
|
|
"Pipenv did not create. Aborting."
|
|
)
|
|
)
|
|
ctx.abort()
|
|
if project.virtualenv_exists:
|
|
loc = project.virtualenv_location
|
|
echo(
|
|
crayons.normal(
|
|
u"{0} ({1})…".format(
|
|
crayons.normal("Removing virtualenv", bold=True),
|
|
crayons.green(loc),
|
|
)
|
|
)
|
|
)
|
|
with spinner():
|
|
# Remove the virtualenv.
|
|
cleanup_virtualenv(bare=True)
|
|
return 0
|
|
else:
|
|
echo(
|
|
crayons.red(
|
|
"No virtualenv has been created for this project yet!",
|
|
bold=True,
|
|
),
|
|
err=True,
|
|
)
|
|
ctx.abort()
|
|
# --two / --three was passed…
|
|
if (state.python or state.three is not None) or site_packages:
|
|
ensure_project(
|
|
three=state.three,
|
|
python=state.python,
|
|
warn=True,
|
|
site_packages=state.site_packages,
|
|
pypi_mirror=state.pypi_mirror,
|
|
clear=state.clear,
|
|
)
|
|
# Check this again before exiting for empty ``pipenv`` command.
|
|
elif ctx.invoked_subcommand is None:
|
|
# Display help to user, if no commands were passed.
|
|
echo(format_help(ctx.get_help()))
|
|
|
|
|
|
@cli.command(
|
|
short_help="Installs provided packages and adds them to Pipfile, or (if no packages are given), installs all packages from Pipfile.",
|
|
context_settings=dict(ignore_unknown_options=True, allow_extra_args=True),
|
|
)
|
|
@system_option
|
|
@code_option
|
|
@deploy_option
|
|
@skip_lock_option
|
|
@install_options
|
|
@pass_state
|
|
@pass_context
|
|
def install(
|
|
ctx,
|
|
state,
|
|
**kwargs
|
|
):
|
|
"""Installs provided packages and adds them to Pipfile, or (if no packages are given), installs all packages from Pipfile."""
|
|
from ..core import do_install
|
|
|
|
retcode = do_install(
|
|
dev=state.installstate.dev,
|
|
three=state.three,
|
|
python=state.python,
|
|
pypi_mirror=state.pypi_mirror,
|
|
system=state.system,
|
|
lock=not state.installstate.skip_lock,
|
|
ignore_pipfile=state.installstate.ignore_pipfile,
|
|
skip_lock=state.installstate.skip_lock,
|
|
requirements=state.installstate.requirementstxt,
|
|
sequential=state.installstate.sequential,
|
|
pre=state.installstate.pre,
|
|
code=state.installstate.code,
|
|
deploy=state.installstate.deploy,
|
|
keep_outdated=state.installstate.keep_outdated,
|
|
selective_upgrade=state.installstate.selective_upgrade,
|
|
index_url=state.index,
|
|
extra_index_url=state.extra_index_urls,
|
|
packages=state.installstate.packages,
|
|
editable_packages=state.installstate.editables,
|
|
)
|
|
if retcode:
|
|
ctx.abort()
|
|
|
|
|
|
@cli.command(short_help="Un-installs a provided package and removes it from Pipfile.")
|
|
@option("--lock", is_flag=True, default=True, help="Lock afterwards.")
|
|
@option(
|
|
"--all-dev",
|
|
is_flag=True,
|
|
default=False,
|
|
help="Un-install all package from [dev-packages].",
|
|
)
|
|
@option(
|
|
"--all",
|
|
is_flag=True,
|
|
default=False,
|
|
help="Purge all package(s) from virtualenv. Does not edit Pipfile.",
|
|
)
|
|
@uninstall_options
|
|
@pass_state
|
|
def uninstall(
|
|
state,
|
|
lock=False,
|
|
all_dev=False,
|
|
all=False,
|
|
**kwargs
|
|
):
|
|
"""Un-installs a provided package and removes it from Pipfile."""
|
|
from ..core import do_uninstall
|
|
|
|
retcode = do_uninstall(
|
|
packages=state.installstate.packages,
|
|
editable_packages=state.installstate.editables,
|
|
three=state.three,
|
|
python=state.python,
|
|
system=state.system,
|
|
lock=lock,
|
|
all_dev=all_dev,
|
|
all=all,
|
|
keep_outdated=state.installstate.keep_outdated,
|
|
pypi_mirror=state.pypi_mirror,
|
|
)
|
|
if retcode:
|
|
sys.exit(retcode)
|
|
|
|
|
|
@cli.command(short_help="Generates Pipfile.lock.")
|
|
@lock_options
|
|
@pass_state
|
|
def lock(
|
|
state,
|
|
**kwargs
|
|
):
|
|
"""Generates Pipfile.lock."""
|
|
from ..core import ensure_project, do_init, do_lock
|
|
|
|
# Ensure that virtualenv is available.
|
|
ensure_project(three=state.three, python=state.python, pypi_mirror=state.pypi_mirror)
|
|
if state.installstate.requirementstxt:
|
|
do_init(dev=state.installstate.dev, requirements=state.installstate.requirementstxt,
|
|
pypi_mirror=state.pypi_mirror)
|
|
do_lock(
|
|
clear=state.clear,
|
|
pre=state.installstate.pre,
|
|
keep_outdated=state.installstate.keep_outdated,
|
|
pypi_mirror=state.pypi_mirror,
|
|
)
|
|
|
|
|
|
@cli.command(
|
|
short_help="Spawns a shell within the virtualenv.",
|
|
context_settings=dict(ignore_unknown_options=True, allow_extra_args=True),
|
|
)
|
|
@option(
|
|
"--fancy",
|
|
is_flag=True,
|
|
default=False,
|
|
help="Run in shell in fancy mode (for elegantly configured shells).",
|
|
)
|
|
@option(
|
|
"--anyway",
|
|
is_flag=True,
|
|
default=False,
|
|
help="Always spawn a subshell, even if one is already spawned.",
|
|
)
|
|
@argument("shell_args", nargs=-1)
|
|
@pypi_mirror_option
|
|
@three_option
|
|
@python_option
|
|
@pass_state
|
|
def shell(
|
|
state,
|
|
fancy=False,
|
|
shell_args=None,
|
|
anyway=False,
|
|
):
|
|
"""Spawns a shell within the virtualenv."""
|
|
from ..core import load_dot_env, do_shell
|
|
|
|
# Prevent user from activating nested environments.
|
|
if "PIPENV_ACTIVE" in os.environ:
|
|
# If PIPENV_ACTIVE is set, VIRTUAL_ENV should always be set too.
|
|
venv_name = os.environ.get("VIRTUAL_ENV", "UNKNOWN_VIRTUAL_ENVIRONMENT")
|
|
if not anyway:
|
|
echo(
|
|
"{0} {1} {2}\nNo action taken to avoid nested environments.".format(
|
|
crayons.normal("Shell for"),
|
|
crayons.green(venv_name, bold=True),
|
|
crayons.normal("already activated.", bold=True),
|
|
),
|
|
err=True,
|
|
)
|
|
sys.exit(1)
|
|
# Load .env file.
|
|
load_dot_env()
|
|
# Use fancy mode for Windows.
|
|
if os.name == "nt":
|
|
fancy = True
|
|
do_shell(
|
|
three=state.three,
|
|
python=state.python,
|
|
fancy=fancy,
|
|
shell_args=shell_args,
|
|
pypi_mirror=state.pypi_mirror,
|
|
)
|
|
|
|
|
|
@cli.command(
|
|
add_help_option=False,
|
|
short_help="Spawns a command installed into the virtualenv.",
|
|
context_settings=dict(
|
|
ignore_unknown_options=True,
|
|
allow_interspersed_args=False,
|
|
allow_extra_args=True,
|
|
),
|
|
)
|
|
@common_options
|
|
@argument("command")
|
|
@argument("args", nargs=-1)
|
|
@pass_state
|
|
def run(state, command, args):
|
|
"""Spawns a command installed into the virtualenv."""
|
|
from ..core import do_run
|
|
|
|
do_run(
|
|
command=command, args=args, three=state.three, python=state.python, pypi_mirror=state.pypi_mirror
|
|
)
|
|
|
|
|
|
@cli.command(
|
|
short_help="Checks for security vulnerabilities and against PEP 508 markers provided in Pipfile.",
|
|
context_settings=dict(ignore_unknown_options=True, allow_extra_args=True),
|
|
)
|
|
@option(
|
|
"--unused",
|
|
nargs=1,
|
|
default=False,
|
|
help="Given a code path, show potentially unused dependencies.",
|
|
)
|
|
@option(
|
|
"--ignore",
|
|
"-i",
|
|
multiple=True,
|
|
help="Ignore specified vulnerability during safety checks.",
|
|
)
|
|
@common_options
|
|
@system_option
|
|
@argument("args", nargs=-1)
|
|
@pass_state
|
|
def check(
|
|
state,
|
|
unused=False,
|
|
style=False,
|
|
ignore=None,
|
|
args=None,
|
|
**kwargs
|
|
):
|
|
"""Checks for security vulnerabilities and against PEP 508 markers provided in Pipfile."""
|
|
from ..core import do_check
|
|
|
|
do_check(
|
|
three=state.three,
|
|
python=state.python,
|
|
system=state.system,
|
|
unused=unused,
|
|
ignore=ignore,
|
|
args=args,
|
|
pypi_mirror=state.pypi_mirror,
|
|
)
|
|
|
|
|
|
@cli.command(short_help="Runs lock, then sync.")
|
|
@option("--bare", is_flag=True, default=False, help="Minimal output.")
|
|
@option(
|
|
"--outdated", is_flag=True, default=False, help=u"List out-of-date dependencies."
|
|
)
|
|
@option("--dry-run", is_flag=True, default=None, help=u"List out-of-date dependencies.")
|
|
@install_options
|
|
@pass_state
|
|
@pass_context
|
|
def update(
|
|
ctx,
|
|
state,
|
|
bare=False,
|
|
dry_run=None,
|
|
outdated=False,
|
|
**kwargs
|
|
):
|
|
"""Runs lock, then sync."""
|
|
from ..core import (
|
|
ensure_project,
|
|
do_outdated,
|
|
do_lock,
|
|
do_sync,
|
|
project,
|
|
)
|
|
|
|
ensure_project(three=state.three, python=state.python, warn=True, pypi_mirror=state.pypi_mirror)
|
|
if not outdated:
|
|
outdated = bool(dry_run)
|
|
if outdated:
|
|
do_outdated(pypi_mirror=state.pypi_mirror)
|
|
packages = [p for p in state.installstate.packages if p]
|
|
editable = [p for p in state.installstate.editables if p]
|
|
if not packages:
|
|
echo(
|
|
"{0} {1} {2} {3}{4}".format(
|
|
crayons.white("Running", bold=True),
|
|
crayons.red("$ pipenv lock", bold=True),
|
|
crayons.white("then", bold=True),
|
|
crayons.red("$ pipenv sync", bold=True),
|
|
crayons.white(".", bold=True),
|
|
)
|
|
)
|
|
else:
|
|
for package in packages + editable:
|
|
if package not in project.all_packages:
|
|
echo(
|
|
"{0}: {1} was not found in your Pipfile! Aborting."
|
|
"".format(
|
|
crayons.red("Warning", bold=True),
|
|
crayons.green(package, bold=True),
|
|
),
|
|
err=True,
|
|
)
|
|
ctx.abort()
|
|
|
|
do_lock(
|
|
clear=state.clear,
|
|
pre=state.installstate.pre,
|
|
keep_outdated=state.installstate.keep_outdated,
|
|
pypi_mirror=state.pypi_mirror,
|
|
)
|
|
do_sync(
|
|
ctx=ctx,
|
|
dev=state.installstate.dev,
|
|
three=state.three,
|
|
python=state.python,
|
|
bare=bare,
|
|
dont_upgrade=not state.installstate.keep_outdated,
|
|
user=False,
|
|
clear=state.clear,
|
|
unused=False,
|
|
sequential=state.installstate.sequential,
|
|
pypi_mirror=state.pypi_mirror,
|
|
)
|
|
|
|
|
|
@cli.command(short_help=u"Displays currently-installed dependency graph information.")
|
|
@option("--bare", is_flag=True, default=False, help="Minimal output.")
|
|
@option("--json", is_flag=True, default=False, help="Output JSON.")
|
|
@option("--json-tree", is_flag=True, default=False, help="Output JSON in nested tree.")
|
|
@option("--reverse", is_flag=True, default=False, help="Reversed dependency graph.")
|
|
def graph(bare=False, json=False, json_tree=False, reverse=False):
|
|
"""Displays currently-installed dependency graph information."""
|
|
from ..core import do_graph
|
|
|
|
do_graph(bare=bare, json=json, json_tree=json_tree, reverse=reverse)
|
|
|
|
|
|
@cli.command(short_help="View a given module in your editor.", name="open")
|
|
@common_options
|
|
@argument("module", nargs=1)
|
|
@pass_state
|
|
def run_open(state, module, *args, **kwargs):
|
|
"""View a given module in your editor."""
|
|
from ..core import which, ensure_project
|
|
|
|
# Ensure that virtualenv is available.
|
|
ensure_project(three=state.three, python=state.python, validate=False,
|
|
pypi_mirror=state.pypi_mirror)
|
|
c = delegator.run(
|
|
'{0} -c "import {1}; print({1}.__file__);"'.format(which("python"), module)
|
|
)
|
|
try:
|
|
assert c.return_code == 0
|
|
except AssertionError:
|
|
echo(crayons.red("Module not found!"))
|
|
sys.exit(1)
|
|
if "__init__.py" in c.out:
|
|
p = os.path.dirname(c.out.strip().rstrip("cdo"))
|
|
else:
|
|
p = c.out.strip().rstrip("cdo")
|
|
echo(crayons.normal("Opening {0!r} in your EDITOR.".format(p), bold=True))
|
|
edit(filename=p)
|
|
return 0
|
|
|
|
|
|
@cli.command(short_help="Installs all packages specified in Pipfile.lock.")
|
|
@option("--bare", is_flag=True, default=False, help="Minimal output.")
|
|
@sync_options
|
|
@pass_state
|
|
@pass_context
|
|
def sync(
|
|
ctx,
|
|
state,
|
|
bare=False,
|
|
user=False,
|
|
unused=False,
|
|
**kwargs
|
|
):
|
|
"""Installs all packages specified in Pipfile.lock."""
|
|
from ..core import do_sync
|
|
|
|
retcode = do_sync(
|
|
ctx=ctx,
|
|
dev=state.installstate.dev,
|
|
three=state.three,
|
|
python=state.python,
|
|
bare=bare,
|
|
dont_upgrade=(not state.installstate.keep_outdated),
|
|
user=user,
|
|
clear=state.clear,
|
|
unused=unused,
|
|
sequential=state.installstate.sequential,
|
|
pypi_mirror=state.pypi_mirror,
|
|
)
|
|
if retcode:
|
|
ctx.abort()
|
|
|
|
|
|
@cli.command(short_help="Uninstalls all packages not specified in Pipfile.lock.")
|
|
@option("--dry-run", is_flag=True, default=False, help="Just output unneeded packages.")
|
|
@verbose_option
|
|
@three_option
|
|
@python_option
|
|
@pass_state
|
|
@pass_context
|
|
def clean(ctx, state, dry_run=False, bare=False, user=False):
|
|
"""Uninstalls all packages not specified in Pipfile.lock."""
|
|
from ..core import do_clean
|
|
do_clean(ctx=ctx, three=state.three, python=state.python, dry_run=dry_run)
|
|
|
|
|
|
# Only invoke the "did you mean" when an argument wasn't passed (it breaks those).
|
|
if "-" not in "".join(sys.argv) and len(sys.argv) > 1:
|
|
cli = DYMCommandCollection(sources=[cli])
|
|
if __name__ == "__main__":
|
|
cli()
|