summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2018-07-22 23:40:28 -0700
committerTodd Gamblin <tgamblin@llnl.gov>2018-07-24 18:35:02 -0700
commit656e935e50945e7e6b52501164c1df9a8e3dbe12 (patch)
tree952cbe662e607d55c793ba6506d1d64e546d0af1
parent674eb00e53e8d317a4b5c22d26aef2c0c7ec1753 (diff)
downloadspack-656e935e50945e7e6b52501164c1df9a8e3dbe12.tar.gz
spack-656e935e50945e7e6b52501164c1df9a8e3dbe12.tar.bz2
spack-656e935e50945e7e6b52501164c1df9a8e3dbe12.tar.xz
spack-656e935e50945e7e6b52501164c1df9a8e3dbe12.zip
core: make `spack.util.crypto` initialization less expensive.
- This hard-codes the hash lengths rather than computing them on import. - Also cleans up the code in `spack.util.crypto` to make it easier to understand.
-rw-r--r--lib/spack/spack/test/url_fetch.py4
-rw-r--r--lib/spack/spack/util/crypto.py88
2 files changed, 54 insertions, 38 deletions
diff --git a/lib/spack/spack/test/url_fetch.py b/lib/spack/spack/test/url_fetch.py
index 2e6e1912bf..fb4165f4b2 100644
--- a/lib/spack/spack/test/url_fetch.py
+++ b/lib/spack/spack/test/url_fetch.py
@@ -52,7 +52,7 @@ def test_fetch(
mock_archive.url
mock_archive.path
- algo = crypto.hashes[checksum_type]()
+ algo = crypto.hash_fun_for_algo(checksum_type)()
with open(mock_archive.archive_file, 'rb') as f:
algo.update(f.read())
checksum = algo.hexdigest()
@@ -145,7 +145,7 @@ def test_from_list_url(mock_packages, config):
def test_hash_detection(checksum_type):
- algo = crypto.hashes[checksum_type]()
+ algo = crypto.hash_fun_for_algo(checksum_type)()
h = 'f' * (algo.digest_size * 2) # hex -> bytes
checker = crypto.Checker(h)
assert checker.hash_name == checksum_type
diff --git a/lib/spack/spack/util/crypto.py b/lib/spack/spack/util/crypto.py
index e364b62064..c2b536d50b 100644
--- a/lib/spack/spack/util/crypto.py
+++ b/lib/spack/spack/util/crypto.py
@@ -28,24 +28,28 @@ import hashlib
import llnl.util.tty as tty
-"""Set of acceptable hashes that Spack will use."""
-_hash_algorithms = [
- 'md5',
- 'sha1',
- 'sha224',
- 'sha256',
- 'sha384',
- 'sha512']
+#: Set of hash algorithms that Spack can use, mapped to digest size in bytes
+hashes = {
+ 'md5': 16,
+ 'sha1': 20,
+ 'sha224': 28,
+ 'sha256': 32,
+ 'sha384': 48,
+ 'sha512': 64
+}
-_deprecated_hash_algorithms = ['md5']
+#: size of hash digests in bytes, mapped to algoritm names
+_size_to_hash = dict((v, k) for k, v in hashes.items())
-hashes = dict()
+#: List of deprecated hash functions. On some systems, these cannot be
+#: used without special options to hashlib.
+_deprecated_hash_algorithms = ['md5']
-"""Index for looking up hasher for a digest."""
-_size_to_hash = dict()
+#: cache of hash functions generated
+_hash_functions = {}
class DeprecatedHash(object):
@@ -65,22 +69,41 @@ class DeprecatedHash(object):
return hashlib.new(self.hash_alg)
-for h in _hash_algorithms:
- try:
- if h in _deprecated_hash_algorithms:
- hash_gen = DeprecatedHash(
- h, tty.debug, disable_security_check=False)
- _size_to_hash[hash_gen(disable_alert=True).digest_size] = hash_gen
+def hash_fun_for_algo(algo):
+ """Get a function that can perform the specified hash algorithm."""
+ hash_gen = _hash_functions.get(algo)
+ if hash_gen is None:
+ if algo in _deprecated_hash_algorithms:
+ try:
+ hash_gen = DeprecatedHash(
+ algo, tty.debug, disable_security_check=False)
+
+ # call once to get a ValueError if usedforsecurity is needed
+ hash_gen(disable_alert=True)
+ except ValueError:
+ # Some systems may support the 'usedforsecurity' option
+ # so try with that (but display a warning when it is used)
+ hash_gen = DeprecatedHash(
+ algo, tty.warn, disable_security_check=True)
else:
- hash_gen = getattr(hashlib, h)
- _size_to_hash[hash_gen().digest_size] = hash_gen
- hashes[h] = hash_gen
- except ValueError:
- # Some systems may support the 'usedforsecurity' option so try with
- # that (but display a warning when it is used)
- hash_gen = DeprecatedHash(h, tty.warn, disable_security_check=True)
- hashes[h] = hash_gen
- _size_to_hash[hash_gen(disable_alert=True).digest_size] = hash_gen
+ hash_gen = getattr(hashlib, algo)
+ _hash_functions[algo] = hash_gen
+
+ return hash_gen
+
+
+def hash_algo_for_digest(hexdigest):
+ """Gets name of the hash algorithm for a hex digest."""
+ bytes = len(hexdigest) / 2
+ if bytes not in _size_to_hash:
+ raise ValueError(
+ 'Spack knows no hash algorithm for this digest: %s' % hexdigest)
+ return _size_to_hash[bytes]
+
+
+def hash_fun_for_digest(hexdigest):
+ """Gets a hash function corresponding to a hex digest."""
+ return hash_fun_for_algo(hash_algo_for_digest(hexdigest))
def checksum(hashlib_algo, filename, **kwargs):
@@ -123,15 +146,8 @@ class Checker(object):
def __init__(self, hexdigest, **kwargs):
self.block_size = kwargs.get('block_size', 2**20)
self.hexdigest = hexdigest
- self.sum = None
-
- bytes = len(hexdigest) / 2
- if bytes not in _size_to_hash:
- raise ValueError(
- 'Spack knows no hash algorithm for this digest: %s'
- % hexdigest)
-
- self.hash_fun = _size_to_hash[bytes]
+ self.sum = None
+ self.hash_fun = hash_fun_for_digest(hexdigest)
@property
def hash_name(self):