From f5771a3aa855887f22dad2ba4a58eef1f158f34f Mon Sep 17 00:00:00 2001 From: Luke Murphy Date: Mon, 5 Sep 2016 13:47:13 +0200 Subject: [PATCH] add `--exclude` option --- CHANGELOG.md | 4 ++++ piptools/resolver.py | 12 +++++++++++- piptools/scripts/compile.py | 5 +++-- tests/test_resolver.py | 7 +++++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 092dcc858..5a781ac73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.7.2 + +- Add `--exclude` option (#333) + # 1.7.1 - Add `--allow-unsafe` option (#377) diff --git a/piptools/resolver.py b/piptools/resolver.py index 2d93e9ff1..64a85829e 100644 --- a/piptools/resolver.py +++ b/piptools/resolver.py @@ -53,7 +53,8 @@ def __str__(self): class Resolver(object): - def __init__(self, constraints, repository, cache=None, prereleases=False, clear_caches=False): + def __init__(self, constraints, repository, cache=None, + exclude=None, prereleases=False, clear_caches=False): """ This class resolves a given set of constraints (a collection of InstallRequirement objects) by consulting the given Repository and the @@ -67,6 +68,7 @@ def __init__(self, constraints, repository, cache=None, prereleases=False, clear self.dependency_cache = cache self.prereleases = prereleases self.clear_caches = clear_caches + self.exclude = exclude @property def constraints(self): @@ -259,6 +261,14 @@ def _iter_dependencies(self, ireq): dependency_strings = self.dependency_cache[ireq] log.debug(' {:25} requires {}'.format(format_requirement(ireq), ', '.join(sorted(dependency_strings, key=lambda s: s.lower())) or '-')) + + if self.exclude: + dep_set, exclude_set = set(dependency_strings), set(self.exclude) + log.debug('Excluding following dependencies:') + for exclude in (dep_set & exclude_set): + log.debug(' {}'.format(exclude)) + dependency_strings = dep_set - exclude_set + for dependency_string in dependency_strings: yield InstallRequirement.from_line(dependency_string) diff --git a/piptools/scripts/compile.py b/piptools/scripts/compile.py index f800de757..053699dc2 100755 --- a/piptools/scripts/compile.py +++ b/piptools/scripts/compile.py @@ -55,10 +55,11 @@ class PipCommand(pip.basecommand.Command): 'Will be derived from input file otherwise.')) @click.option('--allow-unsafe', is_flag=True, default=False, help="Pin packages considered unsafe: pip, setuptools & distribute") +@click.option('--exclude', multiple=True, help="Exclude a package", type=str) @click.argument('src_files', nargs=-1, type=click.Path(exists=True, allow_dash=True)) def cli(verbose, dry_run, pre, rebuild, find_links, index_url, extra_index_url, client_cert, trusted_host, header, index, annotate, upgrade, - output_file, allow_unsafe, src_files): + output_file, allow_unsafe, src_files, exclude): """Compiles requirements.txt from requirements.in specs.""" log.verbose = verbose @@ -159,7 +160,7 @@ def cli(verbose, dry_run, pre, rebuild, find_links, index_url, extra_index_url, try: resolver = Resolver(constraints, repository, prereleases=pre, - clear_caches=rebuild) + clear_caches=rebuild, exclude=exclude) results = resolver.resolve() except PipToolsError as e: log.error(str(e)) diff --git a/tests/test_resolver.py b/tests/test_resolver.py index 2b6de37a8..f68912814 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -73,3 +73,10 @@ def test_resolver(resolver, from_line, input, expected, prereleases): output = resolver(input, prereleases=prereleases).resolve() output = {str(line) for line in output} assert output == {str(line) for line in expected} + + +def test_resolver_with_exclude(resolver, from_line): + input, expected = [from_line('ipython')], ['ipython==2.1.0'] + output = resolver(input, exclude=['gnureadline']).resolve() + output = {str(line) for line in output} + assert output == {str(line) for line in expected}