diff --git a/HISTORY.txt b/HISTORY.txt index 07603beb..24ee32a2 100644 --- a/HISTORY.txt +++ b/HISTORY.txt @@ -1,3 +1,10 @@ +6.1.2: + - Skip validation of Pipfiles, massive speedup for far-away users. +6.1.1: + - Bug fix. +6.1.0: + - Self–updating! Very fancy. $ pipenv update. + - Verbose mode for update, install. 6.0.3: - Major bug fix. - Fix for Daniel Ryan's weird corner case. diff --git a/docs/advanced.rst b/docs/advanced.rst index 3c180b0b..b9b57778 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -253,7 +253,6 @@ $ pipenv lock variables. To activate them, simply create the variable in your shell and pipenv will detect it. - - ``PIPENV_SKIP_VALIDATION`` — Tells Pipenv to skip ``Pipfile`` validation (case-checking) — useful for slow internet connections. - ``PIPENV_SHELL_COMPAT`` — Toggle from our default ``pipenv shell`` mode to classic. (Suggested for use with pyenv). diff --git a/pipenv/__version__.py b/pipenv/__version__.py index e7b7493f..e8d08e9c 100644 --- a/pipenv/__version__.py +++ b/pipenv/__version__.py @@ -3,4 +3,4 @@ # //___/ / / / //___/ / // // / / || / / # // / / // ((____ // / / ||/ / -__version__ = '6.0.3' +__version__ = '6.1.2' diff --git a/pipenv/cli.py b/pipenv/cli.py index 772d1082..60d9a8c7 100644 --- a/pipenv/cli.py +++ b/pipenv/cli.py @@ -73,14 +73,53 @@ def check_for_updates(): current = semver.parse_version_info(__version__) if latest > current: - click.echo('{0}: {1} is now available. You get bonus points for upgrading!'.format( + click.echo('{0}: {1} is now available. You get bonus points for upgrading ($ {})!'.format( crayons.green('Courtesy Notice'), - crayons.yellow('Pipenv v{v.major}.{v.minor}.{v.patch}'.format(v=latest)), + crayons.yellow('Pipenv {v.major}.{v.minor}.{v.patch}'.format(v=latest)), + crayons.red('pipenv update [--user]') ), err=True) except Exception: pass +def enhance(user=False): + r = requests.get('https://pypi.python.org/pypi/pipenv/json', timeout=0.5) + latest = sorted([semver.parse_version_info(v) for v in list(r.json()['releases'].keys())])[-1] + current = semver.parse_version_info(__version__) + + if current < latest: + + # Resolve user site, enable user mode automatically. + c = delegator.run('{0} -m site'.format(sys.executable)) + + for line in c.out.split('\n'): + if line.strip().startswith('ENABLE_USER_SITE'): + can_user = eval(line[len('ENABLE_USER_SITE: '):]) + if line.strip().startswith('USER_SITE'): + user_site = eval(''.join(line[len('USER_SITE: '):].split()[:-1])) + + if can_user: + if user_site in sys.modules['pipenv'].__file__: + user = True + + click.echo('{0}: {1} is now available. Automatically upgrading!'.format( + crayons.green('Courtesy Notice'), + crayons.yellow('Pipenv {v.major}.{v.minor}.{v.patch}'.format(v=latest)), + ), err=True) + + if not user: + sys.argv = ['pip', 'install', '--upgrade', 'pipenv'] + else: + sys.argv = ['pip', 'install', '--user', '--upgrade', 'pipenv'] + + sys.modules['pip'].main() + + click.echo('{0} to {1}!'.format( + crayons.green('Pipenv updated'), + crayons.yellow('{v.major}.{v.minor}.{v.patch}'.format(v=latest)) + )) + + def cleanup_virtualenv(bare=True): """Removes the virtualenv directory from the system.""" @@ -242,7 +281,7 @@ def do_where(virtualenv=False, bare=True): click.echo(location) -def do_install_dependencies(dev=False, only=False, bare=False, requirements=False, allow_global=False, ignore_hashes=False, skip_lock=False): +def do_install_dependencies(dev=False, only=False, bare=False, requirements=False, allow_global=False, ignore_hashes=False, skip_lock=False, verbose=False): """"Executes the install functionality.""" if requirements: @@ -259,6 +298,7 @@ def do_install_dependencies(dev=False, only=False, bare=False, requirements=Fals with open(project.lockfile_location) as f: lockfile = split_vcs(json.load(f)) + # Allow pip to resolve dependencies when in skip-lock mode. no_deps = (not skip_lock) # Install default dependencies, always. @@ -289,7 +329,7 @@ def do_install_dependencies(dev=False, only=False, bare=False, requirements=Fals # pip install: for dep, ignore_hash in progress.bar(deps_list): - c = pip_install(dep, ignore_hashes=ignore_hash, allow_global=allow_global, no_deps=no_deps) + c = pip_install(dep, ignore_hashes=ignore_hash, allow_global=allow_global, no_deps=no_deps, verbose=verbose) if c.return_code != 0: click.echo(crayons.red('An error occured while installing!')) @@ -620,7 +660,7 @@ def do_purge(bare=False, downloads=False, allow_global=False): click.echo(crayons.yellow('Environment now purged and fresh!')) -def do_init(dev=False, requirements=False, allow_global=False, ignore_hashes=False, no_hashes=True, ignore_pipfile=False, skip_lock=False): +def do_init(dev=False, requirements=False, allow_global=False, ignore_hashes=False, no_hashes=True, ignore_pipfile=False, skip_lock=False, verbose=False): """Executes the init functionality.""" ensure_pipfile() @@ -663,14 +703,14 @@ def do_init(dev=False, requirements=False, allow_global=False, ignore_hashes=Fal ignore_hashes = False do_install_dependencies(dev=dev, requirements=requirements, allow_global=allow_global, - ignore_hashes=ignore_hashes, skip_lock=skip_lock) + ignore_hashes=ignore_hashes, skip_lock=skip_lock, verbose=verbose) # Activate virtualenv instructions. if not allow_global: do_activate_virtualenv() -def pip_install(package_name=None, r=None, allow_global=False, ignore_hashes=False, no_deps=True): +def pip_install(package_name=None, r=None, allow_global=False, ignore_hashes=False, no_deps=True, verbose=False): # Create files for hash mode. if (not ignore_hashes) and (r is None): @@ -702,6 +742,10 @@ def pip_install(package_name=None, r=None, allow_global=False, ignore_hashes=Fal no_deps = '--no-deps' if no_deps else '' pip_command = '"{0}" install {3} {1} -i {2}'.format(which_pip(allow_global=allow_global), install_reqs, source['url'], no_deps) + + if verbose: + click.echo('$ {0}'.format(pip_command), err=True) + c = delegator.run(pip_command) if c.return_code == 0: @@ -869,9 +913,10 @@ def cli(ctx, where=False, venv=False, rm=False, bare=False, three=False, python= @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('--ignore-pipfile', is_flag=True, default=False, help="Ignore Pipfile when installing, using the Pipfile.lock.") @click.option('--skip-lock', is_flag=True, default=False, help=u"Ignore locking mechanisms when installing—use the Pipfile, instead.") -def install(package_name=False, more_packages=False, dev=False, three=False, python=False, system=False, lock=True, hashes=True, ignore_pipfile=False, skip_lock=False): +def install(package_name=False, more_packages=False, dev=False, three=False, python=False, system=False, lock=True, hashes=True, ignore_pipfile=False, skip_lock=False, verbose=False): # Automatically use an activated virtualenv. if PIPENV_USE_SYSTEM: @@ -895,7 +940,7 @@ def install(package_name=False, more_packages=False, dev=False, three=False, pyt if package_name is False: click.echo(crayons.yellow('No package provided, installing all dependencies.'), err=True) - do_init(dev=dev, allow_global=system, ignore_hashes=not hashes, ignore_pipfile=ignore_pipfile, skip_lock=skip_lock) + do_init(dev=dev, allow_global=system, ignore_hashes=not hashes, ignore_pipfile=ignore_pipfile, skip_lock=skip_lock, verbose=verbose) sys.exit(0) for package_name in package_names: @@ -903,7 +948,7 @@ def install(package_name=False, more_packages=False, dev=False, three=False, pyt # pip install: with spinner(): - c = pip_install(package_name, ignore_hashes=True, allow_global=system, no_deps=False) + c = pip_install(package_name, ignore_hashes=True, allow_global=system, no_deps=False, verbose=verbose) click.echo(crayons.blue(format_pip_output(c.out))) @@ -1152,6 +1197,7 @@ def run(command, args, three=None, python=False): @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 check(three=None, python=False): + # Ensure that virtualenv is available. ensure_project(three=three, python=python, validate=False) @@ -1181,27 +1227,31 @@ def check(three=None, python=False): click.echo(crayons.green('Passed!')) -@click.command(help="Updates pip to latest version, uninstalls all packages, and re-installs package(s) in [packages] to latest compatible versions.") +@click.command(help="Updates Pipenv & pip to latest, uninstalls all packages, and re-installs package(s) in [packages] to latest compatible versions.") +@click.option('--dont-upgrade', '-d', is_flag=True, default=False, help="Don't upgrade Pipenv & pip.") +@click.option('--user', '-U', is_flag=True, default=False, help="When upgrading Pipenv, use pip --user") +@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.") -def update(dev=False, three=None, python=None, dry_run=False, bare=False): +def update(dev=False, three=None, python=None, dry_run=False, bare=False, dont_upgrade=False, user=False, verbose=False): # Ensure that virtualenv is available. ensure_project(three=three, python=python, validate=False) - # --dry-run if dry_run: + dont_upgrade = True updates = False # Dev packages if not bare: click.echo(crayons.yellow('Checking dependencies...'), err=True) - packages = project.dev_packages - packages.update(project.packages) + packages = project.packages + if dev: + packages.update(project.dev_packages) installed_packages = {} deps = convert_deps_to_pip(packages, r=False) @@ -1220,13 +1270,16 @@ def update(dev=False, three=None, python=None, dry_run=False, bare=False): name = result['name'] installed = result['version'] - latest = installed_packages[name] - if installed != latest: - if not bare: - click.echo('{0}=={1} is availble ({2} installed)!'.format(name, latest, installed)) - else: - click.echo('{0}=={1}'.format(name, latest)) - updates = True + try: + latest = installed_packages[name] + if installed != latest: + if not bare: + click.echo('{0}=={1} is availble ({2} installed)!'.format(name, latest, installed)) + else: + click.echo('{0}=={1}'.format(name, latest)) + updates = True + except KeyError: + pass if not updates and not bare: click.echo(crayons.green('All good!')) @@ -1234,12 +1287,17 @@ def update(dev=False, three=None, python=None, dry_run=False, bare=False): sys.exit(int(updates)) # Update pip to latest version. - ensure_latest_pip() + if not dont_upgrade: + ensure_latest_pip() + + # Upgrade self to latest version. + if not dont_upgrade: + enhance(user=user) click.echo(crayons.yellow('Updating all dependencies from Pipfile...')) do_purge() - do_init(dev=dev) + do_init(dev=dev, verbose=verbose) click.echo(crayons.yellow('All dependencies are now up-to-date!')) diff --git a/pipenv/environments.py b/pipenv/environments.py index e86f48ac..bf0baddc 100644 --- a/pipenv/environments.py +++ b/pipenv/environments.py @@ -29,7 +29,7 @@ PIPENV_USE_SYSTEM = os.environ.get('VIRTUAL_ENV') if 'PIPENV_IGNORE_VIRTUALENVS' PIPENV_USE_HASHES = True # Tells pipenv to skip case-checking (slow internet connections). -PIPENV_SKIP_VALIDATION = os.environ.get('PIPENV_SKIP_VALIDATION') +PIPENV_SKIP_VALIDATION = True # Use shell compatibility mode when using venv in project mode. if PIPENV_VENV_IN_PROJECT: diff --git a/pipenv/utils.py b/pipenv/utils.py index feb7751c..2cd58ae5 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -23,7 +23,7 @@ class PipCommand(pip.basecommand.Command): def shellquote(s): - return "'" + s.replace("'", "'\\''") + "'" + return "'" + s.replace("'", "'\\''") + "'" def clean_pkg_version(version):