More split up

This commit is contained in:
Tzu-ping Chung
2018-06-29 00:39:21 +08:00
parent 5ee7514ebd
commit 96f9151e51
18 changed files with 1134 additions and 1109 deletions
+82 -227
View File
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
import os
import sys
from click import (
from pipenv.patched import crayons
from pipenv.vendor import click_completion
from pipenv.vendor.click import (
argument,
command,
echo,
edit,
group,
Group,
option,
@@ -14,29 +16,24 @@ from click import (
version_option,
BadParameter,
)
from click_didyoumean import DYMCommandCollection
import click_completion
import crayons
import delegator
from .__version__ import __version__
from pipenv.vendor.click_didyoumean import DYMCommandCollection
from . import environments
from .__version__ import __version__
from .utils import is_valid_url
# Enable shell completion.
click_completion.init()
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
class PipenvGroup(Group):
"""Custom Group class provides formatted main help"""
"""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"""
"""Show 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
@@ -45,6 +42,7 @@ class PipenvGroup(Group):
if value and not ctx.resilient_parsing:
if not ctx.invoked_subcommand:
# legit main help
from .operations.help import format_help
echo(format_help(ctx.get_help()))
else:
# legit sub-command help
@@ -76,7 +74,9 @@ def validate_python_path(ctx, param, value):
# 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)
raise BadParameter(
'Expected Python at path {} does not exist'.format(value),
)
return value
@@ -165,120 +165,52 @@ def cli(
completion=False,
):
if completion: # Handle this ASAP to make shell startup fast.
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,
)
sys.exit(1)
print(click_completion.get_code(shell=shell, prog_name='pipenv'))
sys.exit(0)
from .core import (
do_py,
warn_in_virtualenv,
project,
format_help
)
from .operations.options import do_completion
do_completion()
return
if man:
from .utils import system_which
if system_which('man'):
path = os.sep.join([os.path.dirname(__file__), 'pipenv.1'])
os.execle(system_which('man'), 'man', path, os.environ)
else:
echo(
'man does not appear to be available on your system.', err=True
)
from .operations.options import do_man
do_man()
return
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'
)
)
)
sys.exit(0)
from .operations.options import do_envs
do_envs()
return
from .operations.options import warn_in_virtualenv
warn_in_virtualenv()
if ctx.invoked_subcommand is None:
# --where was passed...
if where:
from .operations.where import do_where
do_where(bare=True)
sys.exit(0)
elif py:
do_py()
sys.exit()
# --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,
)
sys.exit(1)
else:
echo(project.virtualenv_location)
sys.exit(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.'
)
)
sys.exit(1)
if project.virtualenv_exists:
loc = project.virtualenv_location
echo(
crayons.normal(
u'{0} ({1})…'.format(
crayons.normal('Removing virtualenv', bold=True),
crayons.green(loc),
)
)
)
# Remove the virtualenv.
# TODO: Where can I better put this import? pipenv.ui?
from .operations._utils import spinner
with spinner():
from .operations.virtualenv import cleanup_virtualenv
cleanup_virtualenv(bare=True)
sys.exit(0)
else:
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:
from .operations.ensure import ensure_project
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.
echo(format_help(ctx.get_help()))
# Pre-hook for subcommands.
if ctx.invoked_subcommand is not None:
# --two / --three was passed...
if (python or three is not None) or site_packages:
from .operations.ensure import ensure_project
ensure_project(
three=three, python=python, warn=True,
site_packages=site_packages,
)
return
if where:
from .operations.where import do_where
do_where(bare=True)
return
if py:
from .operations.options import do_py
do_py()
return
if venv:
from .operations.options import do_venv
do_venv()
return
if rm:
from .operations.options import do_rm
do_rm()
return
# Display help to user if nothing were passed.
from .operations.help import format_help
echo(format_help(ctx.get_help()))
@command(
@@ -486,7 +418,7 @@ def uninstall(
keep_outdated=False,
pypi_mirror=None,
):
from .core import do_uninstall
from .operations.uninstall import do_uninstall
do_uninstall(
package_name=package_name,
@@ -569,12 +501,15 @@ def lock(
pre=False,
keep_outdated=False,
):
from .core import ensure_project, do_init, do_lock
# Ensure that virtualenv is available.
from .operations.ensure import ensure_project
ensure_project(three=three, python=python)
if requirements:
from .operations.init import do_init
do_init(dev=dev, requirements=requirements, pypi_mirror=pypi_mirror)
from .operations.lock import do_lock
do_lock(
verbose=verbose, clear=clear, pre=pre, keep_outdated=keep_outdated, pypi_mirror=pypi_mirror
)
@@ -613,7 +548,6 @@ def lock(
def shell(
three=None, python=False, fancy=False, shell_args=None, anyway=False
):
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.
@@ -630,11 +564,8 @@ def shell(
err=True,
)
sys.exit(1)
# Load .env file.
load_dot_env()
# Use fancy mode for Windows.
if os.name == 'nt':
fancy = True
from .operations.shell import do_shell
do_shell(
three=three, python=python, fancy=fancy, shell_args=shell_args
)
@@ -711,7 +642,7 @@ def check(
ignore=None,
args=None,
):
from .core import do_check
from .operations.check import do_check
do_check(
three=three,
python=python,
@@ -791,9 +722,7 @@ def check(
help=u"List out-of-date dependencies.",
)
@argument('package', default=False)
@pass_context
def update(
ctx,
three=None,
python=False,
pypi_mirror=None,
@@ -810,59 +739,13 @@ def update(
outdated=False,
more_packages=None,
):
from .core import (
ensure_project,
do_outdated,
do_lock,
do_sync,
ensure_lockfile,
do_install,
project,
)
ensure_project(three=three, python=python, warn=True)
if not outdated:
outdated = bool(dry_run)
if outdated:
do_outdated(pypi_mirror=pypi_mirror)
if not package:
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 ([package] + list(more_packages) or []):
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,
)
sys.exit(1)
do_lock(
verbose=verbose, clear=clear, pre=pre, keep_outdated=keep_outdated, pypi_mirror=pypi_mirror
)
do_sync(
ctx=ctx,
dev=dev,
three=three,
python=python,
bare=bare,
dont_upgrade=False,
user=False,
verbose=verbose,
clear=clear,
unused=False,
sequential=sequential,
pypi_mirror=pypi_mirror,
from .operations.update import do_update
do_update(
package, list(more_packages) if more_packages else [],
three=three, python=python,
pypi_mirror=pypi_mirror, verbose=verbose, clear=clear,
keep_outdated=keep_outdated, pre=pre, dev=dev, bare=bare,
sequential=sequential, dry_run=dry_run, outdated=outdated,
)
@@ -876,9 +759,8 @@ def update(
'--reverse', is_flag=True, default=False, help="Reversed dependency graph."
)
def graph(bare=False, json=False, json_tree=False, reverse=False):
from .core import do_graph
do_graph(bare=bare, json=json, json_tree=json_tree, reverse=reverse)
from .operations.graph import do_graph
do_graph(bare=bare, json_=json, json_tree=json_tree, reverse=reverse)
@command(short_help="View a given module in your editor.", name="open")
@@ -897,29 +779,8 @@ def graph(bare=False, json=False, json_tree=False, reverse=False):
)
@argument('module', nargs=1)
def run_open(module, three=None, python=None):
from .core import which, ensure_project
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False)
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)
sys.exit(0)
from .operations.open import do_open
do_open(module, three=three, python=python)
@command(short_help="Installs all packages specified in Pipfile.lock.")
@@ -968,9 +829,7 @@ def run_open(module, three=None, python=None):
default=False,
help="Install dependencies one-at-a-time, instead of concurrently.",
)
@pass_context
def sync(
ctx,
dev=False,
three=None,
python=None,
@@ -984,10 +843,8 @@ def sync(
sequential=False,
pypi_mirror=None,
):
from .core import do_sync
from .operations.sync import do_sync
do_sync(
ctx=ctx,
dev=dev,
three=three,
python=python,
@@ -1032,9 +889,7 @@ def sync(
default=False,
help="Just output unneeded packages.",
)
@pass_context
def clean(
ctx,
three=None,
python=None,
dry_run=False,
@@ -1042,10 +897,9 @@ def clean(
user=False,
verbose=False,
):
from .core import do_clean
from .operations.clean import do_clean
do_clean(
ctx=ctx, three=three, python=python, dry_run=dry_run, verbose=verbose
three=three, python=python, dry_run=dry_run, verbose=verbose
)
@@ -1061,6 +915,7 @@ cli.add_command(shell)
cli.add_command(run)
cli.add_command(update)
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])
+1 -774
View File
@@ -1,48 +1,8 @@
# -*- coding=utf-8 -*-
import os
import sys
import shutil
import signal
import json as simplejson
import click
import crayons
import delegator
from .vendor import pexpect
import pipfile
from .project import Project
from .vendor.requirementslib import Requirement
from .utils import (
is_required_version,
pep423_name,
escape_grouped_arguments,
find_windows_executable,
temp_environ,
fs_str,
)
from ._compat import (
Path
)
from . import pep508checker
from .environments import (
PIPENV_SHELL_FANCY,
PIPENV_USE_SYSTEM,
PIPENV_SHELL,
PIPENV_CACHE_DIR,
)
# Backport required for earlier versions of Python.
if sys.version_info < (3, 3):
from .vendor.backports.shutil_get_terminal_size import get_terminal_size
else:
from shutil import get_terminal_size
# ###################3 I PLAN TO KEEP THESE HERE. #########################
from .utils import system_which
from .utils import find_windows_executable, system_which
# Packages that should be ignored later.
@@ -94,736 +54,3 @@ def which_pip(allow_global=False):
project = Project(which=which)
# ###########################################################################
def parse_download_fname(fname, name):
fname, fextension = os.path.splitext(fname)
if fextension == '.whl':
fname = '-'.join(fname.split('-')[:-3])
if fname.endswith('.tar'):
fname, _ = os.path.splitext(fname)
# Substring out package name (plus dash) from file name to get version.
version = fname[len(name) + 1:]
# Ignore implicit post releases in version number.
if '-' in version and version.split('-')[1].isdigit():
version = version.split('-')[0]
return version
def get_downloads_info(names_map, section):
info = []
p = project.parsed_pipfile
for fname in os.listdir(project.download_location):
# Get name from filename mapping.
name = Requirement.from_line(names_map[fname]).name
# Get the version info from the filenames.
version = parse_download_fname(fname, name)
# Get the hash of each file.
cmd = '{0} hash "{1}"'.format(
escape_grouped_arguments(which_pip()),
os.sep.join([project.download_location, fname]),
)
c = delegator.run(cmd)
hash = c.out.split('--hash=')[1].strip()
# Verify we're adding the correct version from Pipfile
# and not one from a dependency.
specified_version = p[section].get(name, '')
if is_required_version(version, specified_version):
info.append(dict(name=name, version=version, hash=hash))
return info
def activate_virtualenv(source=True):
"""Returns the string to activate a virtualenv."""
# Suffix and source command for other shells.
suffix = ''
command = ' .' if source else ''
# Support for fish shell.
if PIPENV_SHELL and 'fish' in PIPENV_SHELL:
suffix = '.fish'
command = 'source'
# Support for csh shell.
if PIPENV_SHELL and 'csh' in PIPENV_SHELL:
suffix = '.csh'
command = 'source'
# Escape any spaces located within the virtualenv path to allow
# for proper activation.
venv_location = project.virtualenv_location.replace(' ', r'\ ')
if source:
return '{2} {0}/bin/activate{1}'.format(venv_location, suffix, command)
else:
return '{0}/bin/activate'.format(venv_location)
def do_purge(bare=False, downloads=False, allow_global=False, verbose=False):
"""Executes the purge functionality."""
if downloads:
if not bare:
click.echo(
crayons.normal(u'Clearing out downloads directory...', bold=True)
)
shutil.rmtree(project.download_location)
return
freeze = delegator.run(
'{0} freeze'.format(
escape_grouped_arguments(which_pip(allow_global=allow_global))
)
).out
# Remove comments from the output, if any.
installed = [
line
for line in freeze.splitlines()
if not line.lstrip().startswith('#')
]
# Remove setuptools and friends from installed, if present.
for package_name in BAD_PACKAGES:
for i, package in enumerate(installed):
if package.startswith(package_name):
del installed[i]
actually_installed = []
for package in installed:
try:
dep = Requirement.from_line(package)
except AssertionError:
dep = None
if dep and not dep.is_vcs and not dep.editable:
dep = dep.name
actually_installed.append(dep)
if not bare:
click.echo(
u'Found {0} installed package(s), purging...'.format(
len(actually_installed)
)
)
command = '{0} uninstall {1} -y'.format(
escape_grouped_arguments(which_pip(allow_global=allow_global)),
' '.join(actually_installed),
)
if verbose:
click.echo('$ {0}'.format(command))
c = delegator.run(command)
if not bare:
click.echo(crayons.blue(c.out))
click.echo(crayons.green('Environment now purged and fresh!'))
def pip_download(package_name):
cache_dir = Path(PIPENV_CACHE_DIR)
pip_config = {
'PIP_CACHE_DIR': fs_str(cache_dir.as_posix()),
'PIP_WHEEL_DIR': fs_str(cache_dir.joinpath('wheels').as_posix()),
'PIP_DESTINATION_DIR': fs_str(cache_dir.joinpath('pkgs').as_posix()),
}
for source in project.sources:
cmd = '{0} download "{1}" -i {2} -d {3}'.format(
escape_grouped_arguments(which_pip()),
package_name,
source['url'],
project.download_location,
)
c = delegator.run(cmd, env=pip_config)
if c.return_code == 0:
break
return c
def format_help(help):
"""Formats the help string."""
help = help.replace('Options:', str(crayons.normal('Options:', bold=True)))
help = help.replace(
'Usage: pipenv',
str('Usage: {0}'.format(crayons.normal('pipenv', bold=True))),
)
help = help.replace(' check', str(crayons.red(' check', bold=True)))
help = help.replace(' clean', str(crayons.red(' clean', bold=True)))
help = help.replace(' graph', str(crayons.red(' graph', bold=True)))
help = help.replace(
' install', str(crayons.magenta(' install', bold=True))
)
help = help.replace(' lock', str(crayons.green(' lock', bold=True)))
help = help.replace(' open', str(crayons.red(' open', bold=True)))
help = help.replace(' run', str(crayons.yellow(' run', bold=True)))
help = help.replace(' shell', str(crayons.yellow(' shell', bold=True)))
help = help.replace(' sync', str(crayons.green(' sync', bold=True)))
help = help.replace(
' uninstall', str(crayons.magenta(' uninstall', bold=True))
)
help = help.replace(' update', str(crayons.green(' update', bold=True)))
additional_help = """
Usage Examples:
Create a new project using Python 3.6, specifically:
$ {1}
Install all dependencies for a project (including dev):
$ {2}
Create a lockfile containing pre-releases:
$ {6}
Show a graph of your installed dependencies:
$ {4}
Check your installed dependencies for security vulnerabilities:
$ {7}
Install a local setup.py into your virtual environment/Pipfile:
$ {5}
Use a lower-level pip command:
$ {8}
Commands:""".format(
crayons.red('pipenv --three'),
crayons.red('pipenv --python 3.6'),
crayons.red('pipenv install --dev'),
crayons.red('pipenv lock'),
crayons.red('pipenv graph'),
crayons.red('pipenv install -e .'),
crayons.red('pipenv lock --pre'),
crayons.red('pipenv check'),
crayons.red('pipenv run pip freeze'),
)
help = help.replace('Commands:', additional_help)
return help
def warn_in_virtualenv():
if PIPENV_USE_SYSTEM:
# Only warn if pipenv isn't already active.
if 'PIPENV_ACTIVE' not in os.environ:
click.echo(
'{0}: Pipenv found itself running within a virtual environment, '
'so it will automatically use that environment, instead of '
'creating its own for any project. You can set '
'{1} to force pipenv to ignore that environment and create '
'its own instead.'.format(
crayons.green('Courtesy Notice'),
crayons.normal('PIPENV_IGNORE_VIRTUALENVS=1', bold=True),
),
err=True,
)
def ensure_lockfile(keep_outdated=False, pypi_mirror=None):
"""Ensures that the lockfile is up-to-date."""
if not keep_outdated:
keep_outdated = project.settings.get('keep_outdated')
# Write out the lockfile if it doesn't exist, but not if the Pipfile is being ignored
if project.lockfile_exists:
old_hash = project.get_lockfile_hash()
new_hash = project.calculate_pipfile_hash()
if new_hash != old_hash:
click.echo(
crayons.red(
u'Pipfile.lock ({0}) out of date, updating to ({1})...'.format(
old_hash[-6:], new_hash[-6:]
),
bold=True,
),
err=True,
)
do_lock(keep_outdated=keep_outdated, pypi_mirror=pypi_mirror)
else:
do_lock(keep_outdated=keep_outdated, pypi_mirror=pypi_mirror)
def do_py(system=False):
try:
click.echo(which('python', allow_global=system))
except AttributeError:
click.echo(crayons.red('No project found!'))
def do_outdated(pypi_mirror=None):
packages = {}
results = delegator.run('{0} freeze'.format(which('pip'))).out.strip(
).split(
'\n'
)
results = filter(bool, results)
for result in results:
dep = Requirement.from_line(result)
packages.update(dep.as_pipfile())
updated_packages = {}
lockfile = do_lock(write=False, pypi_mirror=pypi_mirror)
for section in ('develop', 'default'):
for package in lockfile[section]:
try:
updated_packages[package] = lockfile[section][package][
'version'
]
except KeyError:
pass
outdated = []
for package in packages:
norm_name = pep423_name(package)
if norm_name in updated_packages:
if updated_packages[norm_name] != packages[package]:
outdated.append(
(package, updated_packages[norm_name], packages[package])
)
for package, new_version, old_version in outdated:
click.echo(
'Package {0!r} out-of-date: {1!r} installed, {2!r} available.'.format(
package, old_version, new_version
)
)
sys.exit(bool(outdated))
def do_uninstall(
package_name=False,
more_packages=False,
three=None,
python=False,
system=False,
lock=False,
all_dev=False,
all=False,
verbose=False,
keep_outdated=False,
pypi_mirror=None,
):
# Automatically use an activated virtualenv.
if PIPENV_USE_SYSTEM:
system = True
# Ensure that virtualenv is available.
ensure_project(three=three, python=python)
package_names = (package_name,) + more_packages
pipfile_remove = True
# Un-install all dependencies, if --all was provided.
if all is True:
click.echo(
crayons.normal(
u'Un-installing all packages from virtualenv...', bold=True
)
)
do_purge(allow_global=system, verbose=verbose)
sys.exit(0)
# Uninstall [dev-packages], if --dev was provided.
if all_dev:
if 'dev-packages' not in project.parsed_pipfile:
click.echo(
crayons.normal(
'No {0} to uninstall.'.format(
crayons.red('[dev-packages]')
),
bold=True,
)
)
sys.exit(0)
click.echo(
crayons.normal(
u'Un-installing {0}...'.format(crayons.red('[dev-packages]')),
bold=True,
)
)
package_names = project.dev_packages.keys()
if package_name is False and not all_dev:
click.echo(crayons.red('No package provided!'), err=True)
sys.exit(1)
for package_name in package_names:
click.echo(u'Un-installing {0}...'.format(crayons.green(package_name)))
cmd = '{0} uninstall {1} -y'.format(
escape_grouped_arguments(which_pip(allow_global=system)),
package_name,
)
if verbose:
click.echo('$ {0}'.format(cmd))
c = delegator.run(cmd)
click.echo(crayons.blue(c.out))
if pipfile_remove:
in_packages = project.get_package_name_in_pipfile(
package_name, dev=False)
in_dev_packages = project.get_package_name_in_pipfile(
package_name, dev=True)
if not in_dev_packages and not in_packages:
click.echo(
'No package {0} to remove from Pipfile.'.format(
crayons.green(package_name)
)
)
continue
click.echo(
u'Removing {0} from Pipfile...'.format(
crayons.green(package_name)
)
)
# Remove package from both packages and dev-packages.
project.remove_package_from_pipfile(package_name, dev=True)
project.remove_package_from_pipfile(package_name, dev=False)
if lock:
do_lock(system=system, keep_outdated=keep_outdated, pypi_mirror=pypi_mirror)
def do_shell(three=None, python=False, fancy=False, shell_args=None):
from .patched.pew import pew
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False)
# Set an environment variable, so we know we're in the environment.
os.environ['PIPENV_ACTIVE'] = '1'
compat = (not fancy)
# Support shell compatibility mode.
if PIPENV_SHELL_FANCY:
compat = False
# Compatibility mode:
if compat:
if PIPENV_SHELL:
shell = os.path.abspath(PIPENV_SHELL)
else:
click.echo(
crayons.red(
'Please ensure that the {0} environment variable '
'is set before activating shell.'.format(
crayons.normal('SHELL', bold=True)
)
),
err=True,
)
sys.exit(1)
click.echo(
crayons.normal(
'Spawning environment shell ({0}). Use {1} to leave.'.format(
crayons.red(shell), crayons.normal("'exit'", bold=True)
),
bold=True,
),
err=True,
)
cmd = "{0} -i'".format(shell)
args = []
# Standard (properly configured shell) mode:
else:
if project.is_venv_in_project():
# use .venv as the target virtualenv name
workon_name = '.venv'
else:
workon_name = project.virtualenv_name
cmd = sys.executable
args = ['-m', 'pipenv.pew', 'workon', workon_name]
# Grab current terminal dimensions to replace the hardcoded default
# dimensions of pexpect
terminal_dimensions = get_terminal_size()
try:
with temp_environ():
if project.is_venv_in_project():
os.environ['WORKON_HOME'] = project.project_directory
c = pexpect.spawn(
cmd,
args,
dimensions=(
terminal_dimensions.lines, terminal_dimensions.columns
),
)
# Windows!
except AttributeError:
# import subprocess
# Tell pew to use the project directory as its workon_home
with temp_environ():
if project.is_venv_in_project():
os.environ['WORKON_HOME'] = project.project_directory
pew.workon_cmd([workon_name])
sys.exit(0)
# Activate the virtualenv if in compatibility mode.
if compat:
c.sendline(activate_virtualenv())
# Send additional arguments to the subshell.
if shell_args:
c.sendline(' '.join(shell_args))
# Handler for terminal resizing events
# Must be defined here to have the shell process in its context, since we
# can't pass it as an argument
def sigwinch_passthrough(sig, data):
terminal_dimensions = get_terminal_size()
c.setwinsize(terminal_dimensions.lines, terminal_dimensions.columns)
signal.signal(signal.SIGWINCH, sigwinch_passthrough)
# Interact with the new shell.
c.interact(escape_character=None)
c.close()
sys.exit(c.exitstatus)
def do_check(three=None, python=False, system=False, unused=False, ignore=None, args=None):
if not system:
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False, warn=False)
if not args:
args = []
if unused:
deps_required = [k for k in project.packages.keys()]
deps_needed = import_from_code(unused)
for dep in deps_needed:
try:
deps_required.remove(dep)
except ValueError:
pass
if deps_required:
click.echo(
crayons.normal(
'The following dependencies appear unused, and may be safe for removal:'
)
)
for dep in deps_required:
click.echo(' - {0}'.format(crayons.green(dep)))
sys.exit(1)
else:
sys.exit(0)
click.echo(crayons.normal(u'Checking PEP 508 requirements...', bold=True))
if system:
python = system_which('python')
else:
python = which('python')
# Run the PEP 508 checker in the virtualenv.
c = delegator.run(
'"{0}" {1}'.format(
python,
escape_grouped_arguments(pep508checker.__file__.rstrip('cdo')),
)
)
results = simplejson.loads(c.out)
# Load the pipfile.
p = pipfile.Pipfile.load(project.pipfile_location)
failed = False
# Assert each specified requirement.
for marker, specifier in p.data['_meta']['requires'].items():
if marker in results:
try:
assert results[marker] == specifier
except AssertionError:
failed = True
click.echo(
'Specifier {0} does not match {1} ({2}).'
''.format(
crayons.green(marker),
crayons.blue(specifier),
crayons.red(results[marker]),
),
err=True,
)
if failed:
click.echo(crayons.red('Failed!'), err=True)
sys.exit(1)
else:
click.echo(crayons.green('Passed!'))
click.echo(
crayons.normal(u'Checking installed package safety...', bold=True)
)
path = pep508checker.__file__.rstrip('cdo')
path = os.sep.join(__file__.split(os.sep)[:-1] + ['patched', 'safety.zip'])
if not system:
python = which('python')
else:
python = system_which('python')
if ignore:
ignored = '--ignore {0}'.format('--ignore '.join(ignore))
click.echo(crayons.normal('Notice: Ignoring CVE(s) {0}'.format(crayons.yellow(', '.join(ignore)))), err=True)
else:
ignored = ''
c = delegator.run(
'"{0}" {1} check --json --key=1ab8d58f-5122e025-83674263-bc1e79e0 {2}'.format(
python, escape_grouped_arguments(path), ignored
)
)
try:
results = simplejson.loads(c.out)
except ValueError:
click.echo('An error occurred:', err=True)
click.echo(c.err, err=True)
sys.exit(1)
for (package, resolved, installed, description, vuln) in results:
click.echo(
'{0}: {1} {2} resolved ({3} installed)!'.format(
crayons.normal(vuln, bold=True),
crayons.green(package),
crayons.red(resolved, bold=False),
crayons.red(installed, bold=True),
)
)
click.echo('{0}'.format(description))
click.echo()
if not results:
click.echo(crayons.green('All good!'))
else:
sys.exit(1)
def do_graph(bare=False, json=False, json_tree=False, reverse=False):
import pipdeptree
try:
python_path = which('python')
except AttributeError:
click.echo(
u'{0}: {1}'.format(
crayons.red('Warning', bold=True),
u'Unable to display currently-installed dependency graph information here. '
u'Please run within a Pipenv project.',
),
err=True,
)
sys.exit(1)
if reverse and json:
click.echo(
u'{0}: {1}'.format(
crayons.red('Warning', bold=True),
u'Using both --reverse and --json together is not supported. '
u'Please select one of the two options.',
),
err=True,
)
sys.exit(1)
if reverse and json_tree:
click.echo(
u'{0}: {1}'.format(
crayons.red('Warning', bold=True),
u'Using both --reverse and --json-tree together is not supported. '
u'Please select one of the two options.',
),
err=True,
)
sys.exit(1)
if json and json_tree:
click.echo(
u'{0}: {1}'.format(
crayons.red('Warning', bold=True),
u'Using both --json and --json-tree together is not supported. '
u'Please select one of the two options.',
),
err=True,
)
sys.exit(1)
flag = ''
if json:
flag = '--json'
if json_tree:
flag = '--json-tree'
if reverse:
flag = '--reverse'
if not project.virtualenv_exists:
click.echo(
u'{0}: No virtualenv has been created for this project yet! Consider '
u'running {1} first to automatically generate one for you or see'
u'{2} for further instructions.'.format(
crayons.red('Warning', bold=True),
crayons.green('`pipenv install`'),
crayons.green('`pipenv install --help`'),
),
err=True,
)
sys.exit(1)
cmd = '"{0}" {1} {2}'.format(
python_path,
escape_grouped_arguments(pipdeptree.__file__.rstrip('cdo')),
flag,
)
# Run dep-tree.
c = delegator.run(cmd)
if not bare:
if json:
data = []
for d in simplejson.loads(c.out):
if d['package']['key'] not in BAD_PACKAGES:
data.append(d)
click.echo(simplejson.dumps(data, indent=4))
sys.exit(0)
elif json_tree:
def traverse(obj):
if isinstance(obj, list):
return [traverse(package) for package in obj if package['key'] not in BAD_PACKAGES]
else:
obj['dependencies'] = traverse(obj['dependencies'])
return obj
data = traverse(simplejson.loads(c.out))
click.echo(simplejson.dumps(data, indent=4))
sys.exit(0)
else:
for line in c.out.split('\n'):
# Ignore bad packages as top level.
if line.split('==')[0] in BAD_PACKAGES and not reverse:
continue
# Bold top-level packages.
if not line.startswith(' '):
click.echo(crayons.normal(line, bold=True))
# Echo the rest.
else:
click.echo(crayons.normal(line, bold=False))
else:
click.echo(c.out)
if c.return_code != 0:
click.echo(
'{0} {1}'.format(
crayons.red('ERROR: ', bold=True),
crayons.white('{0}'.format(c.err, bold=True)),
),
err=True
)
# Return its return code.
sys.exit(c.return_code)
def do_clean(
ctx, three=None, python=None, dry_run=False, bare=False, verbose=False, pypi_mirror=None
):
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False)
ensure_lockfile(pypi_mirror=pypi_mirror)
installed_package_names = []
pip_freeze_command = delegator.run('{0} freeze'.format(which_pip()))
for line in pip_freeze_command.out.split('\n'):
installed = line.strip()
if not installed or installed.startswith('#'): # Comment or empty.
continue
r = Requirement.from_line(installed).requirement
# Ignore editable installations.
if not r.editable:
installed_package_names.append(r.name.lower())
else:
if verbose:
click.echo('Ignoring {0}.'.format(repr(r.name)), err=True)
# Remove known "bad packages" from the list.
for bad_package in BAD_PACKAGES:
if bad_package in installed_package_names:
if verbose:
click.echo('Ignoring {0}.'.format(repr(bad_package)), err=True)
del installed_package_names[
installed_package_names.index(bad_package)
]
# Intelligently detect if --dev should be used or not.
develop = [k.lower() for k in project.lockfile_content['develop'].keys()]
default = [k.lower() for k in project.lockfile_content['default'].keys()]
for used_package in set(develop + default):
if used_package in installed_package_names:
del installed_package_names[
installed_package_names.index(used_package)
]
failure = False
for apparent_bad_package in installed_package_names:
if dry_run:
click.echo(apparent_bad_package)
else:
click.echo(
crayons.white(
'Uninstalling {0}...'.format(repr(apparent_bad_package)),
bold=True,
)
)
# Uninstall the package.
c = delegator.run(
'{0} uninstall {1} -y'.format(
which_pip(), apparent_bad_package
)
)
if c.return_code != 0:
failure = True
sys.exit(int(failure))
+39 -1
View File
@@ -1,12 +1,17 @@
import contextlib
import os
from pipenv.patched import crayons
from pipenv.vendor import blindspin
from pipenv.vendor import blindspin, click
from pipenv.core import BAD_PACKAGES, project
from pipenv.environments import (
PIPENV_COLORBLIND,
PIPENV_DONT_LOAD_ENV,
PIPENV_DOTENV_LOCATION,
PIPENV_NOSPIN,
)
from pipenv.utils import proper_case
# Disable colors, for the color blind and others who do not prefer colors.
@@ -44,3 +49,36 @@ def convert_deps_to_pip(deps, project=None, r=True, include_index=False):
f.write('\n'.join(dependencies).encode('utf-8'))
f.close()
return f.name
def load_dot_env():
"""Loads .env file into sys.environ.
"""
if not PIPENV_DONT_LOAD_ENV:
# If the project doesn't exist yet, check current directory for a .env file
from pipenv.vendor import dotenv
project_directory = project.project_directory or '.'
denv = dotenv.find_dotenv(
PIPENV_DOTENV_LOCATION or os.sep.join([project_directory, '.env'])
)
if os.path.isfile(denv):
click.echo(
crayons.normal(
'Loading .env environment variables...', bold=True
),
err=True,
)
dotenv.load_dotenv(denv, override=True)
def import_from_code(path='.'):
from pipreqs import pipreqs
rs = []
try:
for r in pipreqs.get_all_imports(path):
if r not in BAD_PACKAGES:
rs.append(r)
pkg_names = pipreqs.get_pkg_names(rs)
return [proper_case(r) for r in pkg_names]
except Exception:
return []
+117
View File
@@ -0,0 +1,117 @@
import json
import os
import sys
from pipenv.patched import crayons, pipfile
from pipenv.vendor import click, delegator
from pipenv import pep508checker
from pipenv.core import project, which
from pipenv.utils import escape_grouped_arguments, system_which
from ._utils import import_from_code
from .ensure import ensure_project
def do_check(three=None, python=False, system=False, unused=False, ignore=None, args=None):
if not system:
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False, warn=False)
if not args:
args = []
if unused:
deps_required = [k for k in project.packages.keys()]
deps_needed = import_from_code(unused)
for dep in deps_needed:
try:
deps_required.remove(dep)
except ValueError:
pass
if deps_required:
click.echo(
crayons.normal(
'The following dependencies appear unused, and may be safe for removal:'
)
)
for dep in deps_required:
click.echo(' - {0}'.format(crayons.green(dep)))
sys.exit(1)
else:
sys.exit(0)
click.echo(crayons.normal(u'Checking PEP 508 requirements...', bold=True))
if system:
python = system_which('python')
else:
python = which('python')
# Run the PEP 508 checker in the virtualenv.
c = delegator.run(
'"{0}" {1}'.format(
python,
escape_grouped_arguments(pep508checker.__file__.rstrip('cdo')),
)
)
results = json.loads(c.out)
# Load the pipfile.
p = pipfile.Pipfile.load(project.pipfile_location)
failed = False
# Assert each specified requirement.
for marker, specifier in p.data['_meta']['requires'].items():
if marker in results:
try:
assert results[marker] == specifier
except AssertionError:
failed = True
click.echo(
'Specifier {0} does not match {1} ({2}).'
''.format(
crayons.green(marker),
crayons.blue(specifier),
crayons.red(results[marker]),
),
err=True,
)
if failed:
click.echo(crayons.red('Failed!'), err=True)
sys.exit(1)
else:
click.echo(crayons.green('Passed!'))
click.echo(
crayons.normal(u'Checking installed package safety...', bold=True)
)
path = pep508checker.__file__.rstrip('cdo')
path = os.sep.join(__file__.split(os.sep)[:-1] + ['patched', 'safety.zip'])
if not system:
python = which('python')
else:
python = system_which('python')
if ignore:
ignored = '--ignore {0}'.format('--ignore '.join(ignore))
click.echo(crayons.normal('Notice: Ignoring CVE(s) {0}'.format(crayons.yellow(', '.join(ignore)))), err=True)
else:
ignored = ''
c = delegator.run(
'"{0}" {1} check --json --key=1ab8d58f-5122e025-83674263-bc1e79e0 {2}'.format(
python, escape_grouped_arguments(path), ignored
)
)
try:
results = json.loads(c.out)
except ValueError:
click.echo('An error occurred:', err=True)
click.echo(c.err, err=True)
sys.exit(1)
for (package, resolved, installed, description, vuln) in results:
click.echo(
'{0}: {1} {2} resolved ({3} installed)!'.format(
crayons.normal(vuln, bold=True),
crayons.green(package),
crayons.red(resolved, bold=False),
crayons.red(installed, bold=True),
)
)
click.echo('{0}'.format(description))
click.echo()
if not results:
click.echo(crayons.green('All good!'))
else:
sys.exit(1)
+66
View File
@@ -0,0 +1,66 @@
import sys
from pipenv.patched import crayons
from pipenv.vendor import click, delegator, requirementslib
from pipenv.core import BAD_PACKAGES, project, which_pip
from .ensure import ensure_project, ensure_lockfile
def do_clean(
three=None, python=None, dry_run=False, bare=False, verbose=False,
):
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False)
ensure_lockfile()
installed_package_names = []
pip_freeze_command = delegator.run('{0} freeze'.format(which_pip()))
for line in pip_freeze_command.out.split('\n'):
installed = line.strip()
if not installed or installed.startswith('#'): # Comment or empty.
continue
r = requirementslib.Requirement.from_line(installed).requirement
# Ignore editable installations.
if not r.editable:
installed_package_names.append(r.name.lower())
else:
if verbose:
click.echo('Ignoring {0}.'.format(repr(r.name)), err=True)
# Remove known "bad packages" from the list.
for bad_package in BAD_PACKAGES:
if bad_package in installed_package_names:
if verbose:
click.echo('Ignoring {0}.'.format(repr(bad_package)), err=True)
del installed_package_names[
installed_package_names.index(bad_package)
]
# Intelligently detect if --dev should be used or not.
develop = [k.lower() for k in project.lockfile_content['develop'].keys()]
default = [k.lower() for k in project.lockfile_content['default'].keys()]
for used_package in set(develop + default):
if used_package in installed_package_names:
del installed_package_names[
installed_package_names.index(used_package)
]
failure = False
for apparent_bad_package in installed_package_names:
if dry_run:
click.echo(apparent_bad_package)
else:
click.echo(
crayons.white(
'Uninstalling {0}...'.format(repr(apparent_bad_package)),
bold=True,
)
)
# Uninstall the package.
c = delegator.run(
'{0} uninstall {1} -y'.format(
which_pip(), apparent_bad_package
)
)
if c.return_code != 0:
failure = True
sys.exit(int(failure))
+24
View File
@@ -14,6 +14,7 @@ from pipenv.core import (
from pipenv.environments import (
PIPENV_SKIP_VALIDATION,
PIPENV_USE_SYSTEM,
PIPENV_VIRTUALENV,
PIPENV_YES,
)
from pipenv.utils import get_python_executable_version
@@ -242,3 +243,26 @@ def ensure_project(
sys.exit(1)
# Ensure the Pipfile exists.
ensure_pipfile(validate=validate, skip_requirements=skip_requirements, system=system)
def ensure_lockfile(keep_outdated=False, pypi_mirror=None):
"""Ensures that the lockfile is up-to-date."""
if not keep_outdated:
keep_outdated = project.settings.get('keep_outdated')
# Write out the lockfile if it doesn't exist, but not if the Pipfile is being ignored
if project.lockfile_exists:
old_hash = project.get_lockfile_hash()
new_hash = project.calculate_pipfile_hash()
if new_hash == old_hash:
return
click.echo(
crayons.red(
u'Pipfile.lock ({0}) out of date, updating to ({1})...'.format(
old_hash[-6:], new_hash[-6:]
),
bold=True,
),
err=True,
)
from .lock import do_lock
do_lock(keep_outdated=keep_outdated, pypi_mirror=pypi_mirror)
+122
View File
@@ -0,0 +1,122 @@
import json
import sys
from pipenv.patched import crayons
from pipenv.vendor import click, delegator
from pipenv.core import BAD_PACKAGES, project, which
from pipenv.utils import escape_grouped_arguments
def do_graph(bare=False, json_=False, json_tree=False, reverse=False):
import pipdeptree
try:
python_path = which('python')
except AttributeError:
click.echo(
u'{0}: {1}'.format(
crayons.red('Warning', bold=True),
u'Unable to display currently-installed dependency graph information here. '
u'Please run within a Pipenv project.',
),
err=True,
)
sys.exit(1)
if reverse and json_:
click.echo(
u'{0}: {1}'.format(
crayons.red('Warning', bold=True),
u'Using both --reverse and --json together is not supported. '
u'Please select one of the two options.',
),
err=True,
)
sys.exit(1)
if reverse and json_tree:
click.echo(
u'{0}: {1}'.format(
crayons.red('Warning', bold=True),
u'Using both --reverse and --json-tree together is not supported. '
u'Please select one of the two options.',
),
err=True,
)
sys.exit(1)
if json_ and json_tree:
click.echo(
u'{0}: {1}'.format(
crayons.red('Warning', bold=True),
u'Using both --json and --json-tree together is not supported. '
u'Please select one of the two options.',
),
err=True,
)
sys.exit(1)
flag = ''
if json_:
flag = '--json'
if json_tree:
flag = '--json-tree'
if reverse:
flag = '--reverse'
if not project.virtualenv_exists:
click.echo(
u'{0}: No virtualenv has been created for this project yet! Consider '
u'running {1} first to automatically generate one for you or see'
u'{2} for further instructions.'.format(
crayons.red('Warning', bold=True),
crayons.green('`pipenv install`'),
crayons.green('`pipenv install --help`'),
),
err=True,
)
sys.exit(1)
cmd = '"{0}" {1} {2}'.format(
python_path,
escape_grouped_arguments(pipdeptree.__file__.rstrip('cdo')),
flag,
)
# Run dep-tree.
c = delegator.run(cmd)
if not bare:
if json_:
data = []
for d in json.loads(c.out):
if d['package']['key'] not in BAD_PACKAGES:
data.append(d)
click.echo(json.dumps(data, indent=4))
sys.exit(0)
elif json_tree:
def traverse(obj):
if isinstance(obj, list):
return [traverse(package) for package in obj if package['key'] not in BAD_PACKAGES]
else:
obj['dependencies'] = traverse(obj['dependencies'])
return obj
data = traverse(json.loads(c.out))
click.echo(json.dumps(data, indent=4))
sys.exit(0)
else:
for line in c.out.split('\n'):
# Ignore bad packages as top level.
if line.split('==')[0] in BAD_PACKAGES and not reverse:
continue
# Bold top-level packages.
if not line.startswith(' '):
click.echo(crayons.normal(line, bold=True))
# Echo the rest.
else:
click.echo(crayons.normal(line, bold=False))
else:
click.echo(c.out)
if c.return_code != 0:
click.echo(
'{0} {1}'.format(
crayons.red('ERROR: ', bold=True),
crayons.white('{0}'.format(c.err, bold=True)),
),
err=True
)
# Return its return code.
sys.exit(c.return_code)
+61
View File
@@ -0,0 +1,61 @@
from pipenv.patched import crayons
def format_help(help):
"""Formats the help string."""
help = help.replace('Options:', str(crayons.normal('Options:', bold=True)))
help = help.replace(
'Usage: pipenv',
str('Usage: {0}'.format(crayons.normal('pipenv', bold=True))),
)
help = help.replace(' check', str(crayons.red(' check', bold=True)))
help = help.replace(' clean', str(crayons.red(' clean', bold=True)))
help = help.replace(' graph', str(crayons.red(' graph', bold=True)))
help = help.replace(
' install', str(crayons.magenta(' install', bold=True))
)
help = help.replace(' lock', str(crayons.green(' lock', bold=True)))
help = help.replace(' open', str(crayons.red(' open', bold=True)))
help = help.replace(' run', str(crayons.yellow(' run', bold=True)))
help = help.replace(' shell', str(crayons.yellow(' shell', bold=True)))
help = help.replace(' sync', str(crayons.green(' sync', bold=True)))
help = help.replace(
' uninstall', str(crayons.magenta(' uninstall', bold=True))
)
help = help.replace(' update', str(crayons.green(' update', bold=True)))
additional_help = """
Usage Examples:
Create a new project using Python 3.6, specifically:
$ {1}
Install all dependencies for a project (including dev):
$ {2}
Create a lockfile containing pre-releases:
$ {6}
Show a graph of your installed dependencies:
$ {4}
Check your installed dependencies for security vulnerabilities:
$ {7}
Install a local setup.py into your virtual environment/Pipfile:
$ {5}
Use a lower-level pip command:
$ {8}
Commands:""".format(
crayons.red('pipenv --three'),
crayons.red('pipenv --python 3.6'),
crayons.red('pipenv install --dev'),
crayons.red('pipenv lock'),
crayons.red('pipenv graph'),
crayons.red('pipenv install -e .'),
crayons.red('pipenv lock --pre'),
crayons.red('pipenv check'),
crayons.red('pipenv run pip freeze'),
)
help = help.replace('Commands:', additional_help)
return help
+3 -18
View File
@@ -6,13 +6,12 @@ from pipenv.patched import crayons
from pipenv.vendor import click, requirementslib
from pipenv._compat import TemporaryDirectory
from pipenv.core import BAD_PACKAGES, project
from pipenv.core import project
from pipenv.environments import PIPENV_USE_SYSTEM
from pipenv.utils import (
download_file,
is_star,
is_valid_url,
proper_case,
)
from ._install import (
@@ -21,25 +20,11 @@ from ._install import (
pip_install,
split_argument,
)
from ._utils import convert_deps_to_pip, spinner
from ._utils import convert_deps_to_pip, import_from_code, spinner
from .ensure import ensure_project, import_requirements
from .init import do_init
def _import_from_code(path='.'):
from pipreqs import pipreqs
rs = []
try:
for r in pipreqs.get_all_imports(path):
if r not in BAD_PACKAGES:
rs.append(r)
pkg_names = pipreqs.get_pkg_names(rs)
return [proper_case(r) for r in pkg_names]
except Exception:
return []
def do_install(
package_name=False,
more_packages=False,
@@ -179,7 +164,7 @@ def do_install(
u'Discovering imports from local codebase...', bold=True
)
)
for req in _import_from_code(code):
for req in import_from_code(code):
click.echo(' Found {0}!'.format(crayons.green(req)))
project.add_package_to_pipfile(req)
# Capture -e argument and assign it to following package_name.
+64 -7
View File
@@ -1,3 +1,4 @@
import json
import os
import sys
@@ -7,8 +8,9 @@ except ImportError:
from collections import Mapping
from pipenv.patched import crayons
from pipenv.vendor import click, delegator, six
from pipenv.vendor import click, delegator, requirementslib, six
from pipenv._compat import Path, TemporaryDirectory
from pipenv.core import project, which, which_pip
from pipenv.utils import (
escape_grouped_arguments,
@@ -25,13 +27,68 @@ def _is_pinned(val):
return isinstance(val, six.string_types) and val.startswith('==')
def _obtain_vcs_req(vcs_obj, src_dir, name, rev=None):
target_dir = os.path.join(src_dir, name)
target_rev = vcs_obj.make_rev_options(rev)
if not os.path.exists(target_dir):
vcs_obj.obtain(target_dir)
if not vcs_obj.is_commit_id_equal(target_dir, rev) and not vcs_obj.is_commit_id_equal(target_dir, target_rev):
vcs_obj.update(target_dir, target_rev)
return vcs_obj.get_revision(target_dir)
def _get_vcs_deps(
project,
pip_freeze=None,
which=None,
verbose=False,
clear=False,
pre=False,
allow_global=False,
dev=False,
pypi_mirror=None,
):
from pipenv.patched.notpip._internal.vcs import VcsSupport
section = "vcs_dev_packages" if dev else "vcs_packages"
reqs = []
lockfile = {}
try:
packages = getattr(project, section)
except AttributeError:
return [], []
if not os.environ.get("PIP_SRC") and not project.virtualenv_location:
_src_dir = TemporaryDirectory(prefix='pipenv-', suffix='-src')
src_dir = Path(_src_dir.name)
else:
src_dir = Path(
os.environ.get("PIP_SRC", os.path.join(project.virtualenv_location, "src"))
)
src_dir.mkdir(mode=0o775, exist_ok=True)
vcs_registry = VcsSupport
for pkg_name, pkg_pipfile in packages.items():
requirement = requirementslib.Requirement.from_pipfile(
pkg_name, pkg_pipfile,
)
backend = vcs_registry()._registry.get(requirement.vcs)
__vcs = backend(url=requirement.req.vcs_uri)
locked_rev = None
name = requirement.normalized_name
locked_rev = _obtain_vcs_req(
__vcs, src_dir.as_posix(), name, rev=pkg_pipfile.get("ref")
)
if requirement.is_vcs:
requirement.req.ref = locked_rev
lockfile[name] = requirement.pipfile_entry[1]
reqs.append(requirement)
return reqs, lockfile
def _venv_resolve_deps(
deps, which, project, pre=False, verbose=False, clear=False,
allow_global=False, pypi_mirror=None,
):
from .vendor import delegator
from . import resolver
import json
from pipenv import resolver
if not deps:
return []
resolver = escape_grouped_arguments(resolver.__file__.rstrip('co'))
@@ -145,8 +202,8 @@ def do_lock(
write=True,
pypi_mirror=None,
):
"""Executes the freeze functionality."""
from .utils import get_vcs_deps
"""Executes the freeze functionality.
"""
cached_lockfile = {}
if not pre:
pre = project.settings.get('allow_prereleases')
@@ -227,7 +284,7 @@ def do_lock(
lockfile[settings['lockfile_key']].update(dep_lockfile)
# Add refs for VCS installs.
# TODO: be smarter about this.
vcs_reqs, vcs_lockfile = get_vcs_deps(
vcs_reqs, vcs_lockfile = _get_vcs_deps(
project,
pip_freeze,
which=which,
+32
View File
@@ -0,0 +1,32 @@
import os
import sys
from pipenv.patched import crayons
from pipenv.vendor import click, delegator
from pipenv.core import which
from .ensure import ensure_project
def do_open(module, three, python):
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False)
c = delegator.run(
'{0} -c "import {1}; print({1}.__file__);"'.format(
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)
+134
View File
@@ -0,0 +1,134 @@
"""Various utilities for "pipenv --XXXX".
Global imports should be kept at a minimum to reduce start up time as much
as possible.
"""
import os
import sys
from pipenv.patched import crayons
from pipenv.vendor import click
def do_completion():
from pipenv import shells
from pipenv.vendor import click_completion
try:
shell = shells.detect_info()[0]
except shells.ShellDetectionFailure:
click.echo(
'Fail to detect shell. Please provide the {0} environment '
'variable.'.format(crayons.normal('PIPENV_SHELL', bold=True)),
err=True,
)
sys.exit(1)
print(click_completion.get_code(shell=shell, prog_name='pipenv'))
def do_man():
from pipenv.utils import system_which
man = system_which('man')
if man:
path = os.path.join(os.path.dirname(__file__), 'pipenv.1')
os.execle(man, 'man', path, os.environ)
return # Shouldn't reach here.
click.echo(
'man does not appear to be available on your system.',
err=True,
)
click.get_current_context.exit(1)
def do_envs():
from pipenv import environments
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(
'https://docs.pipenv.org/advanced/'
'#configuration-with-environment-variables'
)),
)
def warn_in_virtualenv():
# Only warn if pipenv isn't already active.
from pipenv.environments import PIPENV_USE_SYSTEM
if not PIPENV_USE_SYSTEM or 'PIPENV_ACTIVE' in os.environ:
return
from pipenv.patched import crayons
from pipenv.vendor import click
click.echo(
'{0}: Pipenv found itself running within a virtual environment, '
'so it will automatically use that environment, instead of '
'creating its own for any project. You can set '
'{1} to force pipenv to ignore that environment and create '
'its own instead.'.format(
crayons.green('Courtesy Notice'),
crayons.normal('PIPENV_IGNORE_VIRTUALENVS=1', bold=True),
),
err=True,
)
def do_py(system=False):
from pipenv.core import which
try:
click.echo(which('python', allow_global=system))
except AttributeError:
click.echo(crayons.red('No project found!'))
def do_venv():
# There is no virtualenv yet.
from pipenv.core import project
if not project.virtualenv_exists:
click.echo(
crayons.red(
'No virtualenv has been created for this project yet!'
),
err=True,
)
sys.exit(1)
click.echo(project.virtualenv_location)
def do_rm():
# Abort if --system (or running in a virtualenv).
from pipenv.environments import PIPENV_USE_SYSTEM
if PIPENV_USE_SYSTEM:
click.echo(
crayons.red(
'You are attempting to remove a virtualenv that '
'Pipenv did not create. Aborting.'
)
)
sys.exit(1)
from pipenv.core import project
if not project.virtualenv_exists:
click.echo(crayons.red(
'No virtualenv has been created for this project yet!',
bold=True,
), err=True)
sys.exit(1)
click.echo(
crayons.normal(
u'{0} ({1})…'.format(
crayons.normal('Removing virtualenv', bold=True),
crayons.green(project.virtualenv_location),
)
)
)
# Remove the virtualenv.
from ._utils import spinner
with spinner():
from .operations.virtualenv import cleanup_virtualenv
cleanup_virtualenv(bare=True)
+3 -25
View File
@@ -2,38 +2,16 @@ import os
import sys
from pipenv.patched import crayons
from pipenv.vendor import click, dotenv
from pipenv.vendor import click
from pipenv.cmdparse import ScriptEmptyError
from pipenv.core import project, which
from pipenv.environments import (
PIPENV_DONT_LOAD_ENV,
PIPENV_DOTENV_LOCATION,
)
from pipenv.utils import system_which
from ._utils import load_dot_env
from .ensure import ensure_project
def _load_dot_env():
"""Loads .env file into sys.environ.
"""
if not PIPENV_DONT_LOAD_ENV:
# If the project doesn't exist yet, check current directory for a .env file
project_directory = project.project_directory or '.'
denv = dotenv.find_dotenv(
PIPENV_DOTENV_LOCATION or os.sep.join([project_directory, '.env'])
)
if os.path.isfile(denv):
click.echo(
crayons.normal(
'Loading .env environment variables...', bold=True
),
err=True,
)
dotenv.load_dotenv(denv, override=True)
def _inline_activate_virtualenv():
try:
activate_this = which('activate_this.py')
@@ -105,7 +83,7 @@ def do_run(command, args, three=None, python=False):
"""
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False)
_load_dot_env()
load_dot_env()
# Activate virtualenv under the current interpreter's environment
_inline_activate_virtualenv()
try:
+138
View File
@@ -0,0 +1,138 @@
import os
import signal
import sys
from pipenv.patched import crayons, pew
from pipenv.vendor import click, pexpect
from pipenv.core import project
from pipenv.environments import (
PIPENV_SHELL,
PIPENV_SHELL_FANCY,
)
from pipenv.utils import temp_environ
from ._utils import load_dot_env
from .ensure import ensure_project
# Backport required for earlier versions of Python.
if sys.version_info < (3, 3):
from .vendor.backports.shutil_get_terminal_size import get_terminal_size
else:
from shutil import get_terminal_size
def _activate_virtualenv(source=True):
"""Returns the string to activate a virtualenv."""
# Suffix and source command for other shells.
suffix = ''
command = ' .' if source else ''
# Support for fish shell.
if PIPENV_SHELL and 'fish' in PIPENV_SHELL:
suffix = '.fish'
command = 'source'
# Support for csh shell.
if PIPENV_SHELL and 'csh' in PIPENV_SHELL:
suffix = '.csh'
command = 'source'
# Escape any spaces located within the virtualenv path to allow
# for proper activation.
venv_location = project.virtualenv_location.replace(' ', r'\ ')
if source:
return '{2} {0}/bin/activate{1}'.format(venv_location, suffix, command)
else:
return '{0}/bin/activate'.format(venv_location)
def do_shell(three=None, python=False, fancy=False, shell_args=None):
# Load .env file.
load_dot_env()
# Use fancy mode for Windows.
if os.name == 'nt':
fancy = True
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False)
# Set an environment variable, so we know we're in the environment.
os.environ['PIPENV_ACTIVE'] = '1'
compat = (not fancy)
# Support shell compatibility mode.
if PIPENV_SHELL_FANCY:
compat = False
# Compatibility mode:
if compat:
if PIPENV_SHELL:
shell = os.path.abspath(PIPENV_SHELL)
else:
click.echo(
crayons.red(
'Please ensure that the {0} environment variable '
'is set before activating shell.'.format(
crayons.normal('SHELL', bold=True)
)
),
err=True,
)
sys.exit(1)
click.echo(
crayons.normal(
'Spawning environment shell ({0}). Use {1} to leave.'.format(
crayons.red(shell), crayons.normal("'exit'", bold=True)
),
bold=True,
),
err=True,
)
cmd = "{0} -i'".format(shell)
args = []
# Standard (properly configured shell) mode:
else:
if project.is_venv_in_project():
# use .venv as the target virtualenv name
workon_name = '.venv'
else:
workon_name = project.virtualenv_name
cmd = sys.executable
args = ['-m', 'pipenv.pew', 'workon', workon_name]
# Grab current terminal dimensions to replace the hardcoded default
# dimensions of pexpect
terminal_dimensions = get_terminal_size()
try:
with temp_environ():
if project.is_venv_in_project():
os.environ['WORKON_HOME'] = project.project_directory
c = pexpect.spawn(
cmd,
args,
dimensions=(
terminal_dimensions.lines, terminal_dimensions.columns
),
)
# Windows!
except AttributeError:
# import subprocess
# Tell pew to use the project directory as its workon_home
with temp_environ():
if project.is_venv_in_project():
os.environ['WORKON_HOME'] = project.project_directory
pew.pew.workon_cmd([workon_name])
sys.exit(0)
# Activate the virtualenv if in compatibility mode.
if compat:
c.sendline(_activate_virtualenv())
# Send additional arguments to the subshell.
if shell_args:
c.sendline(' '.join(shell_args))
# Handler for terminal resizing events
# Must be defined here to have the shell process in its context, since we
# can't pass it as an argument
def sigwinch_passthrough(sig, data):
terminal_dimensions = get_terminal_size()
c.setwinsize(terminal_dimensions.lines, terminal_dimensions.columns)
signal.signal(signal.SIGWINCH, sigwinch_passthrough)
# Interact with the new shell.
c.interact(escape_character=None)
c.close()
sys.exit(c.exitstatus)
-1
View File
@@ -10,7 +10,6 @@ from .init import do_init
def do_sync(
ctx,
dev=False,
three=None,
python=None,
+151
View File
@@ -0,0 +1,151 @@
import shutil
import sys
from pipenv.patched import crayons
from pipenv.vendor import click, delegator, requirementslib
from pipenv.core import BAD_PACKAGES, project, which_pip
from pipenv.environments import PIPENV_USE_SYSTEM
from pipenv.utils import escape_grouped_arguments
from .ensure import ensure_project
from .lock import do_lock
def _purge(bare=False, downloads=False, allow_global=False, verbose=False):
"""Executes the purge functionality."""
if downloads:
if not bare:
click.echo(
crayons.normal(u'Clearing out downloads directory...', bold=True)
)
shutil.rmtree(project.download_location)
return
freeze = delegator.run(
'{0} freeze'.format(
escape_grouped_arguments(which_pip(allow_global=allow_global))
)
).out
# Remove comments from the output, if any.
installed = [
line
for line in freeze.splitlines()
if not line.lstrip().startswith('#')
]
# Remove setuptools and friends from installed, if present.
for package_name in BAD_PACKAGES:
for i, package in enumerate(installed):
if package.startswith(package_name):
del installed[i]
actually_installed = []
for package in installed:
try:
dep = requirementslib.Requirement.from_line(package)
except AssertionError:
dep = None
if dep and not dep.is_vcs and not dep.editable:
dep = dep.name
actually_installed.append(dep)
if not bare:
click.echo(
u'Found {0} installed package(s), purging...'.format(
len(actually_installed)
)
)
command = '{0} uninstall {1} -y'.format(
escape_grouped_arguments(which_pip(allow_global=allow_global)),
' '.join(actually_installed),
)
if verbose:
click.echo('$ {0}'.format(command))
c = delegator.run(command)
if not bare:
click.echo(crayons.blue(c.out))
click.echo(crayons.green('Environment now purged and fresh!'))
def do_uninstall(
package_name=False,
more_packages=False,
three=None,
python=False,
system=False,
lock=False,
all_dev=False,
all=False,
verbose=False,
keep_outdated=False,
pypi_mirror=None,
):
# Automatically use an activated virtualenv.
if PIPENV_USE_SYSTEM:
system = True
# Ensure that virtualenv is available.
ensure_project(three=three, python=python)
package_names = (package_name,) + more_packages
pipfile_remove = True
# Un-install all dependencies, if --all was provided.
if all is True:
click.echo(
crayons.normal(
u'Un-installing all packages from virtualenv...', bold=True
)
)
_purge(allow_global=system, verbose=verbose)
sys.exit(0)
# Uninstall [dev-packages], if --dev was provided.
if all_dev:
if 'dev-packages' not in project.parsed_pipfile:
click.echo(
crayons.normal(
'No {0} to uninstall.'.format(
crayons.red('[dev-packages]')
),
bold=True,
)
)
sys.exit(0)
click.echo(
crayons.normal(
u'Un-installing {0}...'.format(crayons.red('[dev-packages]')),
bold=True,
)
)
package_names = project.dev_packages.keys()
if package_name is False and not all_dev:
click.echo(crayons.red('No package provided!'), err=True)
sys.exit(1)
for package_name in package_names:
click.echo(u'Un-installing {0}...'.format(crayons.green(package_name)))
cmd = '{0} uninstall {1} -y'.format(
escape_grouped_arguments(which_pip(allow_global=system)),
package_name,
)
if verbose:
click.echo('$ {0}'.format(cmd))
c = delegator.run(cmd)
click.echo(crayons.blue(c.out))
if pipfile_remove:
in_packages = project.get_package_name_in_pipfile(
package_name, dev=False)
in_dev_packages = project.get_package_name_in_pipfile(
package_name, dev=True)
if not in_dev_packages and not in_packages:
click.echo(
'No package {0} to remove from Pipfile.'.format(
crayons.green(package_name)
)
)
continue
click.echo(
u'Removing {0} from Pipfile...'.format(
crayons.green(package_name)
)
)
# Remove package from both packages and dev-packages.
project.remove_package_from_pipfile(package_name, dev=True)
project.remove_package_from_pipfile(package_name, dev=False)
if lock:
do_lock(system=system, keep_outdated=keep_outdated, pypi_mirror=pypi_mirror)
+97
View File
@@ -0,0 +1,97 @@
import sys
from pipenv.patched import crayons
from pipenv.vendor import click, delegator, requirementslib
from pipenv.core import project, which
from pipenv.utils import pep423_name
from .ensure import ensure_project
from .lock import do_lock
from .sync import do_sync
def _do_outdated(pypi_mirror=None):
packages = {}
results = delegator.run('{0} freeze'.format(which('pip'))).out.strip(
).split(
'\n'
)
results = filter(bool, results)
for result in results:
dep = requirementslib.Requirement.from_line(result)
packages.update(dep.as_pipfile())
updated_packages = {}
lockfile = do_lock(write=False, pypi_mirror=pypi_mirror)
for section in ('develop', 'default'):
for package in lockfile[section]:
try:
updated_packages[package] = lockfile[section][package][
'version'
]
except KeyError:
pass
outdated = []
for package in packages:
norm_name = pep423_name(package)
if norm_name in updated_packages:
if updated_packages[norm_name] != packages[package]:
outdated.append(
(package, updated_packages[norm_name], packages[package])
)
for package, new_version, old_version in outdated:
click.echo(
'Package {0!r} out-of-date: {1!r} installed, '
'{2!r} available.'.format(package, old_version, new_version),
)
sys.exit(bool(outdated))
def do_update(
package, more_packages, three, python, pypi_mirror, verbose, clear,
keep_outdated, pre, dev, bare, sequential, dry_run, outdated,
):
ensure_project(three=three, python=python, warn=True)
if not outdated:
outdated = bool(dry_run)
if outdated:
_do_outdated(pypi_mirror=pypi_mirror)
if not package:
click.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 ([package] + list(more_packages)):
if package not in project.all_packages:
click.echo(
'{0}: {1} was not found in your Pipfile! Aborting.'
''.format(
crayons.red('Warning', bold=True),
crayons.green(package, bold=True),
),
err=True,
)
sys.exit(1)
do_lock(
verbose=verbose, clear=clear, pre=pre,
keep_outdated=keep_outdated, pypi_mirror=pypi_mirror,
)
do_sync(
dev=dev,
three=three,
python=python,
bare=bare,
dont_upgrade=False,
user=False,
verbose=verbose,
clear=clear,
unused=False,
sequential=sequential,
pypi_mirror=pypi_mirror,
)
-56
View File
@@ -983,62 +983,6 @@ def resolve_ref(vcs_obj, target_dir, ref):
return vcs_obj.get_revision_sha(target_dir, ref)
def obtain_vcs_req(vcs_obj, src_dir, name, rev=None):
target_dir = os.path.join(src_dir, name)
target_rev = vcs_obj.make_rev_options(rev)
if not os.path.exists(target_dir):
vcs_obj.obtain(target_dir)
if not vcs_obj.is_commit_id_equal(target_dir, rev) and not vcs_obj.is_commit_id_equal(target_dir, target_rev):
vcs_obj.update(target_dir, target_rev)
return vcs_obj.get_revision(target_dir)
def get_vcs_deps(
project,
pip_freeze=None,
which=None,
verbose=False,
clear=False,
pre=False,
allow_global=False,
dev=False,
pypi_mirror=None,
):
from .patched.notpip._internal.vcs import VcsSupport
from ._compat import TemporaryDirectory
section = "vcs_dev_packages" if dev else "vcs_packages"
reqs = []
lockfile = {}
try:
packages = getattr(project, section)
except AttributeError:
return [], []
if not os.environ.get("PIP_SRC") and not project.virtualenv_location:
_src_dir = TemporaryDirectory(prefix='pipenv-', suffix='-src')
src_dir = Path(_src_dir.name)
else:
src_dir = Path(
os.environ.get("PIP_SRC", os.path.join(project.virtualenv_location, "src"))
)
src_dir.mkdir(mode=0o775, exist_ok=True)
vcs_registry = VcsSupport
for pkg_name, pkg_pipfile in packages.items():
requirement = Requirement.from_pipfile(pkg_name, pkg_pipfile)
backend = vcs_registry()._registry.get(requirement.vcs)
__vcs = backend(url=requirement.req.vcs_uri)
locked_rev = None
name = requirement.normalized_name
locked_rev = obtain_vcs_req(
__vcs, src_dir.as_posix(), name, rev=pkg_pipfile.get("ref")
)
if requirement.is_vcs:
requirement.req.ref = locked_rev
lockfile[name] = requirement.pipfile_entry[1]
reqs.append(requirement)
return reqs, lockfile
def fs_str(string):
"""Encodes a string into the proper filesystem encoding