comparison lib/python3.8/site-packages/pip/_internal/utils/hashes.py @ 0:9e54283cc701 draft

"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
author guerler
date Mon, 27 Jul 2020 03:47:31 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:9e54283cc701
1 from __future__ import absolute_import
2
3 import hashlib
4
5 from pip._vendor.six import iteritems, iterkeys, itervalues
6
7 from pip._internal.exceptions import (
8 HashMismatch,
9 HashMissing,
10 InstallationError,
11 )
12 from pip._internal.utils.misc import read_chunks
13 from pip._internal.utils.typing import MYPY_CHECK_RUNNING
14
15 if MYPY_CHECK_RUNNING:
16 from typing import (
17 Dict, List, BinaryIO, NoReturn, Iterator
18 )
19 from pip._vendor.six import PY3
20 if PY3:
21 from hashlib import _Hash
22 else:
23 from hashlib import _hash as _Hash
24
25
26 # The recommended hash algo of the moment. Change this whenever the state of
27 # the art changes; it won't hurt backward compatibility.
28 FAVORITE_HASH = 'sha256'
29
30
31 # Names of hashlib algorithms allowed by the --hash option and ``pip hash``
32 # Currently, those are the ones at least as collision-resistant as sha256.
33 STRONG_HASHES = ['sha256', 'sha384', 'sha512']
34
35
36 class Hashes(object):
37 """A wrapper that builds multiple hashes at once and checks them against
38 known-good values
39
40 """
41 def __init__(self, hashes=None):
42 # type: (Dict[str, List[str]]) -> None
43 """
44 :param hashes: A dict of algorithm names pointing to lists of allowed
45 hex digests
46 """
47 self._allowed = {} if hashes is None else hashes
48
49 @property
50 def digest_count(self):
51 # type: () -> int
52 return sum(len(digests) for digests in self._allowed.values())
53
54 def is_hash_allowed(
55 self,
56 hash_name, # type: str
57 hex_digest, # type: str
58 ):
59 # type: (...) -> bool
60 """Return whether the given hex digest is allowed."""
61 return hex_digest in self._allowed.get(hash_name, [])
62
63 def check_against_chunks(self, chunks):
64 # type: (Iterator[bytes]) -> None
65 """Check good hashes against ones built from iterable of chunks of
66 data.
67
68 Raise HashMismatch if none match.
69
70 """
71 gots = {}
72 for hash_name in iterkeys(self._allowed):
73 try:
74 gots[hash_name] = hashlib.new(hash_name)
75 except (ValueError, TypeError):
76 raise InstallationError('Unknown hash name: %s' % hash_name)
77
78 for chunk in chunks:
79 for hash in itervalues(gots):
80 hash.update(chunk)
81
82 for hash_name, got in iteritems(gots):
83 if got.hexdigest() in self._allowed[hash_name]:
84 return
85 self._raise(gots)
86
87 def _raise(self, gots):
88 # type: (Dict[str, _Hash]) -> NoReturn
89 raise HashMismatch(self._allowed, gots)
90
91 def check_against_file(self, file):
92 # type: (BinaryIO) -> None
93 """Check good hashes against a file-like object
94
95 Raise HashMismatch if none match.
96
97 """
98 return self.check_against_chunks(read_chunks(file))
99
100 def check_against_path(self, path):
101 # type: (str) -> None
102 with open(path, 'rb') as file:
103 return self.check_against_file(file)
104
105 def __nonzero__(self):
106 # type: () -> bool
107 """Return whether I know any known-good hashes."""
108 return bool(self._allowed)
109
110 def __bool__(self):
111 # type: () -> bool
112 return self.__nonzero__()
113
114
115 class MissingHashes(Hashes):
116 """A workalike for Hashes used when we're missing a hash for a requirement
117
118 It computes the actual hash of the requirement and raises a HashMissing
119 exception showing it to the user.
120
121 """
122 def __init__(self):
123 # type: () -> None
124 """Don't offer the ``hashes`` kwarg."""
125 # Pass our favorite hash in to generate a "gotten hash". With the
126 # empty list, it will never match, so an error will always raise.
127 super(MissingHashes, self).__init__(hashes={FAVORITE_HASH: []})
128
129 def _raise(self, gots):
130 # type: (Dict[str, _Hash]) -> NoReturn
131 raise HashMissing(gots[FAVORITE_HASH].hexdigest())