| import hashlib |
| import logging |
| import sys |
| from optparse import Values |
| from typing import List |
| |
| from pip._internal.cli.base_command import Command |
| from pip._internal.cli.status_codes import ERROR, SUCCESS |
| from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES |
| from pip._internal.utils.misc import read_chunks, write_output |
| |
| logger = logging.getLogger(__name__) |
| |
| |
| class HashCommand(Command): |
| """ |
| Compute a hash of a local package archive. |
| |
| These can be used with --hash in a requirements file to do repeatable |
| installs. |
| """ |
| |
| usage = "%prog [options] <file> ..." |
| ignore_require_venv = True |
| |
| def add_options(self) -> None: |
| self.cmd_opts.add_option( |
| "-a", |
| "--algorithm", |
| dest="algorithm", |
| choices=STRONG_HASHES, |
| action="store", |
| default=FAVORITE_HASH, |
| help="The hash algorithm to use: one of {}".format( |
| ", ".join(STRONG_HASHES) |
| ), |
| ) |
| self.parser.insert_option_group(0, self.cmd_opts) |
| |
| def run(self, options: Values, args: List[str]) -> int: |
| if not args: |
| self.parser.print_usage(sys.stderr) |
| return ERROR |
| |
| algorithm = options.algorithm |
| for path in args: |
| write_output( |
| "%s:\n--hash=%s:%s", path, algorithm, _hash_of_file(path, algorithm) |
| ) |
| return SUCCESS |
| |
| |
| def _hash_of_file(path: str, algorithm: str) -> str: |
| """Return the hash digest of a file.""" |
| with open(path, "rb") as archive: |
| hash = hashlib.new(algorithm) |
| for chunk in read_chunks(archive): |
| hash.update(chunk) |
| return hash.hexdigest() |