import re import os.path import hashlib from collections import namedtuple Digest = namedtuple('Digest', ['is_tarsum', 'tarsum_version', 'hash_alg', 'hash_bytes']) DIGEST_PATTERN = r'(tarsum\.(v[\w]+)\+)?([\w]+):([0-9a-f]+)' DIGEST_REGEX = re.compile(DIGEST_PATTERN) class InvalidDigestException(RuntimeError): pass def parse_digest(digest): """ Returns the digest parsed out to its components. """ match = DIGEST_REGEX.match(digest) if match is None or match.end() != len(digest): raise InvalidDigestException('Not a valid digest: %s', digest) is_tarsum = match.group(1) is not None return Digest(is_tarsum, match.group(2), match.group(3), match.group(4)) def content_path(digest): """ Returns a relative path to the parsed digest. """ parsed = parse_digest(digest) components = [] if parsed.is_tarsum: components.extend(['tarsum', parsed.tarsum_version]) prefix = parsed.hash_bytes[0:2].zfill(2) components.extend([parsed.hash_alg, prefix, parsed.hash_bytes]) return os.path.join(*components) def sha256_digest(content): """ Returns a sha256 hash of the content bytes in digest form. """ digest = hashlib.sha256(content) return 'sha256:{0}'.format(digest.hexdigest())