Files
pipenv/pipenv/cli.py
T
kennethreitz 4c83c9ea2b declare an encoding
Signed-off-by: Kenneth Reitz <me@kennethreitz.org>
2018-02-04 14:16:53 -05:00

404 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
import os
import sys
import click
import click_completion
import crayons
import delegator
from click_didyoumean import DYMCommandCollection
from .__version__ import __version__
from . import environments
from .environments import *
xyzzy = """
_______ __ __
/ \ / | / |
$$$$$$$ |$$/ ______ ______ _______ __ __ $$ |
$$ |__$$ |/ | / \ / \ / \ / \ / |$$ |
$$ $$/ $$ |/$$$$$$ |/$$$$$$ |$$$$$$$ |$$ \ /$$/ $$ |
$$$$$$$/ $$ |$$ | $$ |$$ $$ |$$ | $$ | $$ /$$/ $$/
$$ | $$ |$$ |__$$ |$$$$$$$$/ $$ | $$ | $$ $$/ __
$$ | $$ |$$ $$/ $$ |$$ | $$ | $$$/ / |
$$/ $$/ $$$$$$$/ $$$$$$$/ $$/ $$/ $/ $$/
$$ |
$$ |
$$/
"""
# Enable shell completion.
click_completion.init()
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
@click.group(invoke_without_command=True, context_settings=CONTEXT_SETTINGS)
@click.option('--update', is_flag=True, default=False, help="Update Pipenv & pip to latest.")
@click.option('--where', is_flag=True, default=False, help="Output project home information.")
@click.option('--venv', is_flag=True, default=False, help="Output virtualenv information.")
@click.option('--py', is_flag=True, default=False, help="Output Python interpreter information.")
@click.option('--envs', is_flag=True, default=False, help="Output Environment Variable options.")
@click.option('--rm', is_flag=True, default=False, help="Remove the virtualenv.")
@click.option('--bare', is_flag=True, default=False, help="Minimal output.")
@click.option('--completion', is_flag=True, default=False, help="Output completion (to be eval'd).")
@click.option('--man', is_flag=True, default=False, help="Display manpage.")
@click.option('--three/--two', is_flag=True, default=None, help="Use Python 3/2 when creating virtualenv.")
@click.option('--python', default=False, nargs=1, help="Specify which version of Python virtualenv should use.")
@click.option('--site-packages', is_flag=True, default=False, help="Enable site-packages for the virtualenv.")
@click.option('--jumbotron', is_flag=True, default=False, help="An easter egg, effectively.")
@click.version_option(prog_name=crayons.normal('pipenv', bold=True), version=__version__)
@click.pass_context
def cli(
ctx, where=False, venv=False, rm=False, bare=False, three=False,
python=False, help=False, update=False, jumbotron=False, py=False,
site_packages=False, envs=False, man=False, completion=False
):
from . import core
if jumbotron:
# Awesome sauce.
click.echo(crayons.normal(xyzzy, bold=True))
if not update:
if core.need_update_check():
# Spun off in background thread, not unlike magic.
core.check_for_updates()
else:
# Update pip to latest version.
core.ensure_latest_pip()
# Upgrade self to latest version.
core.ensure_latest_self()
sys.exit()
if completion:
if PIPENV_SHELL:
os.environ['_PIPENV_COMPLETE'] = 'source-{0}'.format(PIPENV_SHELL.split(os.sep)[-1])
else:
click.echo(
'Please ensure that the {0} environment variable '
'is set.'.format(crayons.normal('SHELL', bold=True)), err=True)
sys.exit(1)
c = delegator.run('pipenv')
click.echo(c.out)
sys.exit(0)
if man:
if core.system_which('man'):
path = os.sep.join([os.path.dirname(__file__), 'pipenv.1'])
os.execle(core.system_which('man'), 'man', path, os.environ)
else:
click.echo('man does not appear to be available on your system.', err=True)
if envs:
click.echo('The following environment variables can be set, to do various things:\n')
for key in environments.__dict__:
if key.startswith('PIPENV'):
click.echo(' - {0}'.format(crayons.normal(key, bold=True)))
click.echo('\nYou can learn more at:\n {0}'.format(
crayons.green('http://docs.pipenv.org/advanced.html#configuration-with-environment-variables')
))
sys.exit(0)
core.warn_in_virtualenv()
if ctx.invoked_subcommand is None:
# --where was passed...
if where:
core.do_where(bare=True)
sys.exit(0)
elif py:
core.do_py()
sys.exit()
# --venv was passed...
elif venv:
# There is no virtualenv yet.
if not core.project.virtualenv_exists:
click.echo(crayons.red('No virtualenv has been created for this project yet!'), err=True)
sys.exit(1)
else:
click.echo(core.project.virtualenv_location)
sys.exit(0)
# --rm was passed...
elif rm:
# Abort if --system (or running in a virtualenv).
if PIPENV_USE_SYSTEM:
click.echo(
crayons.red(
'You are attempting to remove a virtualenv that '
'Pipenv did not create. Aborting.'
)
)
sys.exit(1)
if core.project.virtualenv_exists:
loc = core.project.virtualenv_location
click.echo(
crayons.normal(
u'{0} ({1})…'.format(
crayons.normal('Removing virtualenv', bold=True),
crayons.green(loc)
)
)
)
with core.spinner():
# Remove the virtualenv.
core.cleanup_virtualenv(bare=True)
sys.exit(0)
else:
click.echo(
crayons.red(
'No virtualenv has been created for this project yet!',
bold=True
), err=True
)
sys.exit(1)
# --two / --three was passed...
if (python or three is not None) or site_packages:
core.ensure_project(three=three, python=python, warn=True, site_packages=site_packages)
# Check this again before exiting for empty ``pipenv`` command.
elif ctx.invoked_subcommand is None:
# Display help to user, if no commands were passed.
click.echo(core.format_help(ctx.get_help()))
@click.command(short_help="Installs provided packages and adds them to Pipfile, or (if none is given), installs all packages.", context_settings=dict(
ignore_unknown_options=True,
allow_extra_args=True
))
@click.argument('package_name', default=False)
@click.argument('more_packages', nargs=-1)
@click.option('--dev', '-d', is_flag=True, default=False, help="Install package(s) in [dev-packages].")
@click.option('--three/--two', is_flag=True, default=None, help="Use Python 3/2 when creating virtualenv.")
@click.option('--python', default=False, nargs=1, help="Specify which version of Python virtualenv should use.")
@click.option('--system', is_flag=True, default=False, help="System pip management.")
@click.option('--requirements', '-r', nargs=1, default=False, help="Import a requirements.txt file.")
@click.option('--code', '-c', nargs=1, default=False, help="Import from codebase.")
@click.option('--verbose', is_flag=True, default=False, help="Verbose mode.")
@click.option('--ignore-pipfile', is_flag=True, default=False, help="Ignore Pipfile when installing, using the Pipfile.lock.")
@click.option('--sequential', is_flag=True, default=False, help="Install dependencies one-at-a-time, instead of concurrently.")
@click.option('--skip-lock', is_flag=True, default=False, help=u"Ignore locking mechanisms when installing—use the Pipfile, instead.")
@click.option('--deploy', is_flag=True, default=False, help=u"Abort if the Pipfile.lock is outofdate, or Python version is wrong.")
@click.option('--pre', is_flag=True, default=False, help=u"Allow prereleases.")
def install(
package_name=False, more_packages=False, dev=False, three=False,
python=False, system=False, lock=True, ignore_pipfile=False,
skip_lock=False, verbose=False, requirements=False, sequential=False,
pre=False, code=False, deploy=False
):
from . import core
core.do_install(
package_name=package_name, more_packages=more_packages, dev=dev,
three=three, python=python, system=system, lock=lock,
ignore_pipfile=ignore_pipfile, skip_lock=skip_lock, verbose=verbose,
requirements=requirements, sequential=sequential, pre=pre, code=code,
deploy=deploy
)
@click.command(short_help="Un-installs a provided package and removes it from Pipfile.")
@click.argument('package_name', default=False)
@click.argument('more_packages', nargs=-1)
@click.option('--three/--two', is_flag=True, default=None, help="Use Python 3/2 when creating virtualenv.")
@click.option('--python', default=False, nargs=1, help="Specify which version of Python virtualenv should use.")
@click.option('--system', is_flag=True, default=False, help="System pip management.")
@click.option('--verbose', is_flag=True, default=False, help="Verbose mode.")
@click.option('--lock', is_flag=True, default=True, help="Lock afterwards.")
@click.option('--all-dev', is_flag=True, default=False, help="Un-install all package from [dev-packages].")
@click.option('--all', is_flag=True, default=False, help="Purge all package(s) from virtualenv. Does not edit Pipfile.")
def uninstall(
package_name=False, more_packages=False, three=None, python=False,
system=False, lock=False, all_dev=False, all=False, verbose=False
):
from . import core
core.do_uninstall(
package_name=package_name, more_packages=more_packages, three=three,
python=python, system=system, lock=lock, all_dev=all_dev, all=all,
verbose=verbose
)
@click.command(short_help="Generates Pipfile.lock.")
@click.option('--three/--two', is_flag=True, default=None, help="Use Python 3/2 when creating virtualenv.")
@click.option('--python', default=False, nargs=1, help="Specify which version of Python virtualenv should use.")
@click.option('--verbose', is_flag=True, default=False, help="Verbose mode.")
@click.option('--requirements', '-r', is_flag=True, default=False, help="Generate output compatible with requirements.txt.")
@click.option('--dev', '-d', is_flag=True, default=False, help="Generate output compatible with requirements.txt for the development dependencies.")
@click.option('--clear', is_flag=True, default=False, help="Clear the dependency cache.")
@click.option('--pre', is_flag=True, default=False, help=u"Allow prereleases.")
def lock(three=None, python=False, verbose=False, requirements=False, dev=False, clear=False, pre=False):
from . import core
# Ensure that virtualenv is available.
core.ensure_project(three=three, python=python)
# Load the --pre settings from the Pipfile.
if not pre:
pre = core.project.settings.get('pre')
if requirements:
core.do_init(dev=dev, requirements=requirements)
core.do_lock(verbose=verbose, clear=clear, pre=pre)
@click.command(short_help="Spawns a shell within the virtualenv.", context_settings=dict(
ignore_unknown_options=True,
allow_extra_args=True
))
@click.option('--three/--two', is_flag=True, default=None, help="Use Python 3/2 when creating virtualenv.")
@click.option('--python', default=False, nargs=1, help="Specify which version of Python virtualenv should use.")
@click.option('--fancy', is_flag=True, default=False, help="Run in shell in fancy mode (for elegantly configured shells).")
@click.option('--anyway', is_flag=True, default=False, help="Always spawn a subshell, even if one is already spawned.")
@click.argument('shell_args', nargs=-1)
def shell(three=None, python=False, fancy=False, shell_args=None, anyway=False):
from . import core
# 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:
click.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.
core.load_dot_env()
# Use fancy mode for Windows.
if os.name == 'nt':
fancy = True
core.do_shell(three=three, python=python, fancy=fancy, shell_args=shell_args)
@click.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
)
)
@click.argument('command')
@click.argument('args', nargs=-1)
@click.option('--three/--two', is_flag=True, default=None, help="Use Python 3/2 when creating virtualenv.")
@click.option('--python', default=False, nargs=1, help="Specify which version of Python virtualenv should use.")
def run(command, args, three=None, python=False):
from . import core
core.do_run(command=command, args=args, three=three, python=python)
@click.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
))
@click.option('--three/--two', is_flag=True, default=None, help="Use Python 3/2 when creating virtualenv.")
@click.option('--python', default=False, nargs=1, help="Specify which version of Python virtualenv should use.")
@click.option('--unused', nargs=1, default=False, help="Given a code path, show potentially unused dependencies.")
@click.option('--style', nargs=1, default=False, help="Given a code path, show Flake8 errors.")
@click.argument('args', nargs=-1)
def check(three=None, python=False, unused=False, style=False, args=None):
from . import core
core.do_check(three=three, python=python, unused=unused, style=style, args=args)
@click.command(short_help=u"Displays currentlyinstalled dependency graph information.")
@click.option('--bare', is_flag=True, default=False, help="Minimal output.")
@click.option('--json', is_flag=True, default=False, help="Output JSON.")
@click.option('--reverse', is_flag=True, default=False, help="Reversed dependency graph.")
def graph(bare=False, json=False, reverse=False):
from . import core
core.do_graph(bare=bare, json=json, reverse=reverse)
@click.command(short_help="View a given module in your editor.", name="open")
@click.option('--three/--two', is_flag=True, default=None, help="Use Python 3/2 when creating virtualenv.")
@click.option('--python', default=False, nargs=1, help="Specify which version of Python virtualenv should use.")
@click.argument('module', nargs=1)
def run_open(module, three=None, python=None):
from . import core
# Ensure that virtualenv is available.
core.ensure_project(three=three, python=python, validate=False)
c = delegator.run('{0} -c "import {1}; print({1}.__file__);"'.format(core.which('python'), module))
try:
assert c.return_code == 0
except AssertionError:
click.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')
click.echo(crayons.normal('Opening {0!r} in your EDITOR.'.format(p), bold=True))
click.edit(filename=p)
sys.exit(0)
@click.command(short_help="Uninstalls all packages, and re-installs package(s) in [packages] to latest compatible versions.")
@click.argument('package_name', default=False)
@click.option('--verbose', '-v', is_flag=True, default=False, help="Verbose mode.")
@click.option('--dev', '-d', is_flag=True, default=False, help="Additionally install package(s) in [dev-packages].")
@click.option('--three/--two', is_flag=True, default=None, help="Use Python 3/2 when creating virtualenv.")
@click.option('--python', default=False, nargs=1, help="Specify which version of Python virtualenv should use.")
@click.option('--dry-run', is_flag=True, default=False, help="Just output outdated packages.")
@click.option('--bare', is_flag=True, default=False, help="Minimal output.")
@click.option('--clear', is_flag=True, default=False, help="Clear the dependency cache.")
@click.option('--sequential', is_flag=True, default=False, help="Install dependencies one-at-a-time, instead of concurrently.")
@click.pass_context
def update(
ctx, dev=False, three=None, python=None, dry_run=False, bare=False,
dont_upgrade=False, user=False, verbose=False, clear=False, unused=False,
package_name=None, sequential=False
):
from . import core
core.do_update(
ctx=ctx, install=install, dev=dev, three=three, python=python, dry_run=dry_run,
bare=bare, dont_upgrade=dont_upgrade, user=user, verbose=verbose,
clear=clear, unused=unused, package_name=package_name,
sequential=sequential
)
# Install click commands.
cli.add_command(graph)
cli.add_command(install)
cli.add_command(uninstall)
cli.add_command(update)
cli.add_command(lock)
cli.add_command(check)
cli.add_command(shell)
cli.add_command(run)
cli.add_command(run_open)
# 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()