Path: blob/master/venv/Lib/site-packages/pip/_internal/utils/hashes.py
811 views
from __future__ import absolute_import12import hashlib34from pip._vendor.six import iteritems, iterkeys, itervalues56from pip._internal.exceptions import (7HashMismatch,8HashMissing,9InstallationError,10)11from pip._internal.utils.misc import read_chunks12from pip._internal.utils.typing import MYPY_CHECK_RUNNING1314if MYPY_CHECK_RUNNING:15from typing import (16Dict, List, BinaryIO, NoReturn, Iterator17)18from pip._vendor.six import PY319if PY3:20from hashlib import _Hash21else:22from hashlib import _hash as _Hash232425# The recommended hash algo of the moment. Change this whenever the state of26# the art changes; it won't hurt backward compatibility.27FAVORITE_HASH = 'sha256'282930# Names of hashlib algorithms allowed by the --hash option and ``pip hash``31# Currently, those are the ones at least as collision-resistant as sha256.32STRONG_HASHES = ['sha256', 'sha384', 'sha512']333435class Hashes(object):36"""A wrapper that builds multiple hashes at once and checks them against37known-good values3839"""40def __init__(self, hashes=None):41# type: (Dict[str, List[str]]) -> None42"""43:param hashes: A dict of algorithm names pointing to lists of allowed44hex digests45"""46self._allowed = {} if hashes is None else hashes4748@property49def digest_count(self):50# type: () -> int51return sum(len(digests) for digests in self._allowed.values())5253def is_hash_allowed(54self,55hash_name, # type: str56hex_digest, # type: str57):58# type: (...) -> bool59"""Return whether the given hex digest is allowed."""60return hex_digest in self._allowed.get(hash_name, [])6162def check_against_chunks(self, chunks):63# type: (Iterator[bytes]) -> None64"""Check good hashes against ones built from iterable of chunks of65data.6667Raise HashMismatch if none match.6869"""70gots = {}71for hash_name in iterkeys(self._allowed):72try:73gots[hash_name] = hashlib.new(hash_name)74except (ValueError, TypeError):75raise InstallationError(76'Unknown hash name: {}'.format(hash_name)77)7879for chunk in chunks:80for hash in itervalues(gots):81hash.update(chunk)8283for hash_name, got in iteritems(gots):84if got.hexdigest() in self._allowed[hash_name]:85return86self._raise(gots)8788def _raise(self, gots):89# type: (Dict[str, _Hash]) -> NoReturn90raise HashMismatch(self._allowed, gots)9192def check_against_file(self, file):93# type: (BinaryIO) -> None94"""Check good hashes against a file-like object9596Raise HashMismatch if none match.9798"""99return self.check_against_chunks(read_chunks(file))100101def check_against_path(self, path):102# type: (str) -> None103with open(path, 'rb') as file:104return self.check_against_file(file)105106def __nonzero__(self):107# type: () -> bool108"""Return whether I know any known-good hashes."""109return bool(self._allowed)110111def __bool__(self):112# type: () -> bool113return self.__nonzero__()114115116class MissingHashes(Hashes):117"""A workalike for Hashes used when we're missing a hash for a requirement118119It computes the actual hash of the requirement and raises a HashMissing120exception showing it to the user.121122"""123def __init__(self):124# type: () -> None125"""Don't offer the ``hashes`` kwarg."""126# Pass our favorite hash in to generate a "gotten hash". With the127# empty list, it will never match, so an error will always raise.128super(MissingHashes, self).__init__(hashes={FAVORITE_HASH: []})129130def _raise(self, gots):131# type: (Dict[str, _Hash]) -> NoReturn132raise HashMissing(gots[FAVORITE_HASH].hexdigest())133134135