diff --git a/pipenv/cli.py b/pipenv/cli.py index a089e3c8..6a2d4b1c 100644 --- a/pipenv/cli.py +++ b/pipenv/cli.py @@ -225,7 +225,8 @@ def uninstall( @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 pre–releases.") -def lock(three=None, python=False, verbose=False, requirements=False, dev=False, clear=False, pre=False): +@click.option('--keep-outdated', is_flag=True, default=False, help=u"Keep out–dated dependencies from being updated.") +def lock(three=None, python=False, verbose=False, requirements=False, dev=False, clear=False, pre=False, keep_outdated=False): from . import core # Ensure that virtualenv is available. core.ensure_project(three=three, python=python) @@ -237,7 +238,7 @@ def lock(three=None, python=False, verbose=False, requirements=False, dev=False, if requirements: core.do_init(dev=dev, requirements=requirements) - core.do_lock(verbose=verbose, clear=clear, pre=pre) + core.do_lock(verbose=verbose, clear=clear, pre=pre, keep_outdated=keep_outdated) diff --git a/pipenv/core.py b/pipenv/core.py index e36f7ef1..4695a448 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -34,7 +34,7 @@ from .utils import ( proper_case, pep423_name, split_file, merge_deps, resolve_deps, shellquote, is_vcs, python_version, find_windows_executable, is_file, prepare_pip_source_args, temp_environ, is_valid_url, download_file, get_requirement, need_update_check, - touch_update_stamp + touch_update_stamp, is_pinned ) from .__version__ import __version__ from . import pep508checker, progress @@ -1009,9 +1009,18 @@ def get_downloads_info(names_map, section): return info -def do_lock(verbose=False, system=False, clear=False, pre=False): +def do_lock(verbose=False, system=False, clear=False, pre=False, keep_outdated=False): """Executes the freeze functionality.""" + cached_lockfile = {} + if keep_outdated: + if not project.lockfile_exists: + click.echo('{0}: Pipfile.lock must exist to use --keep-outdated!'.format( + crayons.red('Warning', bold=True) + )) + sys.exit(1) + cached_lockfile = project.lockfile_content + project.destroy_lockfile() # Alert the user of progress. @@ -1152,6 +1161,13 @@ def do_lock(verbose=False, system=False, clear=False, pre=False): except IndexError: pass + # Support for --keep-outdated… + if keep_outdated: + for section_name, section in (('default', project.packages), ('develop', project.dev_packages)): + for package_specified in section: + if not is_pinned(section[package_specified]): + lockfile[section_name][package_specified] = cached_lockfile[section_name][package_specified] + # Run the PEP 508 checker in the virtualenv, add it to the lockfile. cmd = '"{0}" {1}'.format(which('python', allow_global=system), shellquote(pep508checker.__file__.rstrip('cdo'))) c = delegator.run(cmd) diff --git a/pipenv/utils.py b/pipenv/utils.py index ebe1d939..9e41a0c0 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -515,15 +515,17 @@ def convert_deps_from_pip(dep): del dependency[key] return dependency +def is_star(val): + return isinstance(val, six.string_types) and val == '*' + +def is_pinned(val): + return isinstance(val, six.string_types) and val.startswith('==') def convert_deps_to_pip(deps, project=None, r=True, include_index=False): """"Converts a Pipfile-formatted dependency to a pip-formatted one.""" dependencies = [] - def is_star(val): - return isinstance(val, six.string_types) and val == '*' - for dep in deps.keys(): # Default (e.g. '>1.10').