From f38fc903f27aff9d7a903f65d3c51aca8384b740 Mon Sep 17 00:00:00 2001 From: Erik Rose Date: Wed, 21 Oct 2015 15:50:57 -0400 Subject: [PATCH] Obey --require-hashes option in requirements files. Removed the mention of "package index options" in the docs, because they don't all fit that category anymore. Not even --no-binary and --only-binary do; they're "install options". --- docs/reference/pip_install.rst | 5 ++++- pip/basecommand.py | 3 +++ pip/req/req_file.py | 6 ++++++ pip/req/req_set.py | 4 ++-- tests/unit/test_req.py | 18 +++++++++++++++++- 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/docs/reference/pip_install.rst b/docs/reference/pip_install.rst index 955fd877e..dcde6c98c 100644 --- a/docs/reference/pip_install.rst +++ b/docs/reference/pip_install.rst @@ -101,7 +101,7 @@ and the newline following it is effectively ignored. Comments are stripped *before* line continuations are processed. -Additionally, the following Package Index Options are supported: +The following options are supported: * :ref:`-i, --index-url <--index-url>` * :ref:`--extra-index-url <--extra-index-url>` @@ -109,6 +109,7 @@ Additionally, the following Package Index Options are supported: * :ref:`-f, --find-links <--find-links>` * :ref:`--no-binary ` * :ref:`--only-binary ` + * :ref:`--require-hashes <--require-hashes>` For example, to specify :ref:`--no-index <--no-index>` and 2 :ref:`--find-links <--find-links>` locations: @@ -486,6 +487,8 @@ against any requirement not only checks that hash but also activates a global * ``--egg`` is disallowed, because it delegates installation of dependencies to setuptools, giving up pip's ability to enforce any of the above. +.. _`--require-hashes`: + Hash-checking mode can be forced on with the ``--require-hashes`` command-line option:: diff --git a/pip/basecommand.py b/pip/basecommand.py index 3627e9143..ce3ad40eb 100644 --- a/pip/basecommand.py +++ b/pip/basecommand.py @@ -280,6 +280,9 @@ class RequirementCommand(Command): wheel_cache=wheel_cache): found_req_in_file = True requirement_set.add_requirement(req) + # If --require-hashes was a line in a requirements file, tell + # RequirementSet about it: + requirement_set.require_hashes = options.require_hashes if not (args or options.editables or found_req_in_file): opts = {'name': name} diff --git a/pip/req/req_file.py b/pip/req/req_file.py index ff1c1a4a1..defbd7aaf 100644 --- a/pip/req/req_file.py +++ b/pip/req/req_file.py @@ -46,6 +46,7 @@ SUPPORTED_OPTIONS = [ cmdoptions.pre, cmdoptions.process_dependency_links, cmdoptions.trusted_host, + cmdoptions.require_hashes, ] # options to be passed to requirements @@ -123,6 +124,7 @@ def process_line(line, filename, line_number, finder=None, comes_from=None, affect the finder. :param constraint: If True, parsing a constraints file. + :param options: OptionParser options that we may update """ parser = build_parser() defaults = parser.get_default_values() @@ -187,6 +189,10 @@ def process_line(line, filename, line_number, finder=None, comes_from=None, for req in parser: yield req + # percolate hash-checking option upward + elif opts.require_hashes: + options.require_hashes = opts.require_hashes + # set finder options elif finder: if opts.allow_external: diff --git a/pip/req/req_set.py b/pip/req/req_set.py index 24bd338d6..10312dff2 100644 --- a/pip/req/req_set.py +++ b/pip/req/req_set.py @@ -190,7 +190,7 @@ class RequirementSet(object): wheel_download_dir = normalize_path(wheel_download_dir) self.wheel_download_dir = wheel_download_dir self._wheel_cache = wheel_cache - self._require_hashes = require_hashes + self.require_hashes = require_hashes # Maps from install_req -> dependencies_of_install_req self._dependencies = defaultdict(list) @@ -331,7 +331,7 @@ class RequirementSet(object): # If any top-level requirement has a hash specified, enter # hash-checking mode, which requires hashes from all. root_reqs = self.unnamed_requirements + self.requirements.values() - require_hashes = (self._require_hashes or + require_hashes = (self.require_hashes or any(req.has_hash_options for req in root_reqs)) if require_hashes and self.as_egg: raise InstallationError( diff --git a/tests/unit/test_req.py b/tests/unit/test_req.py index a0488e096..6ddfb5d43 100644 --- a/tests/unit/test_req.py +++ b/tests/unit/test_req.py @@ -6,6 +6,7 @@ import tempfile import pytest from mock import Mock, patch, mock_open +from pip.commands.install import InstallCommand from pip.exceptions import (PreviousBuildDirError, InvalidWheelFilename, UnsupportedWheel) from pip.download import path_to_url, PipSession @@ -16,7 +17,7 @@ from pip.req.req_file import process_line from pip.req.req_install import parse_editable from pip.utils import read_text_file from pip._vendor import pkg_resources -from tests.lib import assert_raises_regexp +from tests.lib import assert_raises_regexp, requirements_file class TestRequirementSet(object): @@ -125,6 +126,21 @@ class TestRequirementSet(object): reqset.prepare_files, finder) + def test_missing_hash_with_require_hashes_in_reqs_file(self, data, tmpdir): + """--require-hashes in a requirements file should make its way to the + RequirementSet. + """ + req_set = self.basic_reqset(require_hashes=False) + session = PipSession() + finder = PackageFinder([data.find_links], [], session=session) + command = InstallCommand() + with requirements_file('--require-hashes', tmpdir) as reqs_file: + options, args = command.parse_args(['-r', reqs_file]) + command.populate_requirement_set( + req_set, args, options, finder, session, command.name, + wheel_cache=None) + assert req_set.require_hashes + def test_unsupported_hashes(self, data): """VCS and dir links should raise errors when --require-hashes is on.