Skip to content

Commit

Permalink
Use --pypi-mirror during virtualenv initialization
Browse files Browse the repository at this point in the history
Adds support for the --pypi-mirror parameter for all operations which
may result in a virtualenv initialization.

When a virtualenv is initialized, pip attempts to download several
dependencies from PyPI. If PyPI is unavailable, virtualenv silently uses
local packages instead, which is acceptable in most cases. However, in
some environments connection attempts to PyPI will stall rather than
fail, causing a pipenv timeout. By using the mirror specified by
--pypi-mirror, we can ensure virtualenv will attempt to download
dependencies from an accessible mirror instead of PyPI.

- Fixes #2455
  • Loading branch information
JacobHenner committed Jun 28, 2018
1 parent 8256c53 commit f050a5a
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 26 deletions.
58 changes: 48 additions & 10 deletions pipenv/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ def validate_pypi_mirror(ctx, param, value):
default=False,
help="Enable site-packages for the virtualenv.",
)
@option(
'--pypi-mirror',
default=environments.PIPENV_PYPI_MIRROR,
nargs=1,
callback=validate_pypi_mirror,
help="Specify a PyPI mirror.",
)
@version_option(
prog_name=crayons.normal('pipenv', bold=True), version=__version__
)
Expand All @@ -163,6 +170,7 @@ def cli(
envs=False,
man=False,
completion=False,
pypi_mirror=None,
):
if completion: # Handle this ASAP to make shell startup fast.
from . import shells
Expand Down Expand Up @@ -272,7 +280,7 @@ def cli(
# --two / --three was passed...
if (python or three is not None) or site_packages:
ensure_project(
three=three, python=python, warn=True, site_packages=site_packages
three=three, python=python, warn=True, site_packages=site_packages, pypi_mirror=pypi_mirror
)
# Check this again before exiting for empty ``pipenv`` command.
elif ctx.invoked_subcommand is None:
Expand Down Expand Up @@ -571,7 +579,7 @@ def lock(
from .core import ensure_project, do_init, do_lock

# Ensure that virtualenv is available.
ensure_project(three=three, python=python)
ensure_project(three=three, python=python, pypi_mirror=pypi_mirror)
if requirements:
do_init(dev=dev, requirements=requirements, pypi_mirror=pypi_mirror)
do_lock(
Expand Down Expand Up @@ -608,9 +616,16 @@ def lock(
default=False,
help="Always spawn a subshell, even if one is already spawned.",
)
@option(
'--pypi-mirror',
default=environments.PIPENV_PYPI_MIRROR,
nargs=1,
callback=validate_pypi_mirror,
help="Specify a PyPI mirror.",
)
@argument('shell_args', nargs=-1)
def shell(
three=None, python=False, fancy=False, shell_args=None, anyway=False
three=None, python=False, fancy=False, shell_args=None, anyway=False, pypi_mirror=None
):
from .core import load_dot_env, do_shell
# Prevent user from activating nested environments.
Expand All @@ -635,7 +650,7 @@ def shell(
if os.name == 'nt':
fancy = True
do_shell(
three=three, python=python, fancy=fancy, shell_args=shell_args
three=three, python=python, fancy=fancy, shell_args=shell_args, pypi_mirror=pypi_mirror
)


Expand Down Expand Up @@ -663,9 +678,16 @@ def shell(
callback=validate_python_path,
help="Specify which version of Python virtualenv should use.",
)
def run(command, args, three=None, python=False):
@option(
'--pypi-mirror',
default=environments.PIPENV_PYPI_MIRROR,
nargs=1,
callback=validate_pypi_mirror,
help="Specify a PyPI mirror.",
)
def run(command, args, three=None, python=False, pypi_mirror=None):
from .core import do_run
do_run(command=command, args=args, three=three, python=python)
do_run(command=command, args=args, three=three, python=python, pypi_mirror=pypi_mirror)


@command(
Expand Down Expand Up @@ -700,6 +722,13 @@ def run(command, args, three=None, python=False):
multiple=True,
help="Ignore specified vulnerability during safety checks."
)
@option(
'--pypi-mirror',
default=environments.PIPENV_PYPI_MIRROR,
nargs=1,
callback=validate_pypi_mirror,
help="Specify a PyPI mirror.",
)
@argument('args', nargs=-1)
def check(
three=None,
Expand All @@ -709,6 +738,7 @@ def check(
style=False,
ignore=None,
args=None,
pypi_mirror=None,
):
from .core import do_check
do_check(
Expand All @@ -717,7 +747,8 @@ def check(
system=system,
unused=unused,
ignore=ignore,
args=args
args=args,
pypi_mirror=pypi_mirror
)


Expand Down Expand Up @@ -819,7 +850,7 @@ def update(
project,
)

ensure_project(three=three, python=python, warn=True)
ensure_project(three=three, python=python, warn=True, pypi_mirror=pypi_mirror)
if not outdated:
outdated = bool(dry_run)
if outdated:
Expand Down Expand Up @@ -894,12 +925,19 @@ def graph(bare=False, json=False, json_tree=False, reverse=False):
callback=validate_python_path,
help="Specify which version of Python virtualenv should use.",
)
@option(
'--pypi-mirror',
default=environments.PIPENV_PYPI_MIRROR,
nargs=1,
callback=validate_pypi_mirror,
help="Specify a PyPI mirror.",
)
@argument('module', nargs=1)
def run_open(module, three=None, python=None):
def run_open(module, three=None, python=None, pypi_mirror=None):
from .core import which, ensure_project

# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False)
ensure_project(three=three, python=python, validate=False, pypi_mirror=pypi_mirror)
c = delegator.run(
'{0} -c "import {1}; print({1}.__file__);"'.format(
which('python'), module
Expand Down
35 changes: 19 additions & 16 deletions pipenv/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ def activate_pyenv():
return path_to_python


def ensure_virtualenv(three=None, python=None, site_packages=False):
def ensure_virtualenv(three=None, python=None, site_packages=False, pypi_mirror=None):
"""Creates a virtualenv, if one doesn't exist."""

def abort():
Expand All @@ -571,7 +571,7 @@ def abort():
)
)
sys.exit(1)
do_create_virtualenv(python=python, site_packages=site_packages)
do_create_virtualenv(python=python, site_packages=site_packages, pypi_mirror=pypi_mirror)
except KeyboardInterrupt:
# If interrupted, cleanup the virtualenv.
cleanup_virtualenv(bare=False)
Expand Down Expand Up @@ -599,7 +599,7 @@ def abort():
cleanup_virtualenv(bare=True)
# Call this function again.
ensure_virtualenv(
three=three, python=python, site_packages=site_packages
three=three, python=python, site_packages=site_packages, pypi_mirror=pypi_mirror
)


Expand All @@ -612,6 +612,7 @@ def ensure_project(
site_packages=False,
deploy=False,
skip_requirements=False,
pypi_mirror=None,
):
"""Ensures both Pipfile and virtualenv exist for the project."""
# Automatically use an activated virtualenv.
Expand All @@ -622,7 +623,7 @@ def ensure_project(
# Skip virtualenv creation when --system was used.
if not system:
ensure_virtualenv(
three=three, python=python, site_packages=site_packages
three=three, python=python, site_packages=site_packages, pypi_mirror=pypi_mirror
)
if warn:
# Warn users if they are using the wrong version of Python.
Expand Down Expand Up @@ -885,7 +886,7 @@ def convert_three_to_python(three, python):
return python


def do_create_virtualenv(python=None, site_packages=False):
def do_create_virtualenv(python=None, site_packages=False, pypi_mirror=None):
"""Creates a virtualenv."""
click.echo(
crayons.normal(u'Creating a virtualenv for this project...', bold=True),
Expand Down Expand Up @@ -933,7 +934,8 @@ def do_create_virtualenv(python=None, site_packages=False):
# Actually create the virtualenv.
with spinner():
try:
c = delegator.run(cmd, block=False, timeout=PIPENV_TIMEOUT)
pip_config = {'PIP_INDEX_URL': pypi_mirror} if pypi_mirror else {}
c = delegator.run(cmd, block=False, timeout=PIPENV_TIMEOUT, env=pip_config)
except OSError:
click.echo(
'{0}: it looks like {1} is not in your {2}. '
Expand Down Expand Up @@ -1264,7 +1266,7 @@ def do_init(
if not system:
if not project.virtualenv_exists:
try:
do_create_virtualenv()
do_create_virtualenv(pypi_mirror=pypi_mirror)
except KeyboardInterrupt:
cleanup_virtualenv(bare=False)
sys.exit(1)
Expand Down Expand Up @@ -1789,6 +1791,7 @@ def do_install(
warn=True,
deploy=deploy,
skip_requirements=skip_requirements,
pypi_mirror=pypi_mirror,
)
# Load the --pre settings from the Pipfile.
if not pre:
Expand Down Expand Up @@ -2097,7 +2100,7 @@ def do_uninstall(
if PIPENV_USE_SYSTEM:
system = True
# Ensure that virtualenv is available.
ensure_project(three=three, python=python)
ensure_project(three=three, python=python, pypi_mirror=pypi_mirror)
package_names = (package_name,) + more_packages
pipfile_remove = True
# Un-install all dependencies, if --all was provided.
Expand Down Expand Up @@ -2166,11 +2169,11 @@ def do_uninstall(
do_lock(system=system, keep_outdated=keep_outdated, pypi_mirror=pypi_mirror)


def do_shell(three=None, python=False, fancy=False, shell_args=None):
def do_shell(three=None, python=False, fancy=False, shell_args=None, pypi_mirror=None):
from .patched.pew import pew

# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False)
ensure_project(three=three, python=python, validate=False, pypi_mirror=pypi_mirror)
# Set an environment variable, so we know we're in the environment.
os.environ['PIPENV_ACTIVE'] = '1'
compat = (not fancy)
Expand Down Expand Up @@ -2320,13 +2323,13 @@ def do_run_posix(script, command):
os.execl(command_path, command_path, *script.args)


def do_run(command, args, three=None, python=False):
def do_run(command, args, three=None, python=False, pypi_mirror=None):
"""Attempt to run command either pulling from project or interpreting as executable.
Args are appended to the command in [scripts] section of project if found.
"""
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False)
ensure_project(three=three, python=python, validate=False, pypi_mirror=pypi_mirror)
load_dot_env()
# Activate virtualenv under the current interpreter's environment
inline_activate_virtualenv()
Expand All @@ -2340,10 +2343,10 @@ def do_run(command, args, three=None, python=False):
do_run_posix(script, command=command)


def do_check(three=None, python=False, system=False, unused=False, ignore=None, args=None):
def do_check(three=None, python=False, system=False, unused=False, ignore=None, args=None, pypi_mirror=None):
if not system:
# Ensure that virtualenv is available.
ensure_project(three=three, python=python, validate=False, warn=False)
ensure_project(three=three, python=python, validate=False, warn=False, pypi_mirror=pypi_mirror)
if not args:
args = []
if unused:
Expand Down Expand Up @@ -2586,7 +2589,7 @@ def do_sync(
sys.exit(1)

# Ensure that virtualenv is available if not system.
ensure_project(three=three, python=python, validate=False, deploy=deploy)
ensure_project(three=three, python=python, validate=False, deploy=deploy, pypi_mirror=pypi_mirror)

# Install everything.
requirements_dir = TemporaryDirectory(
Expand All @@ -2610,7 +2613,7 @@ 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_project(three=three, python=python, validate=False, pypi_mirror=pypi_mirror)
ensure_lockfile(pypi_mirror=pypi_mirror)

installed_package_names = []
Expand Down

0 comments on commit f050a5a

Please sign in to comment.