def digest(self, path=os.curdir, hasher=utils.DEFAULT_HASHER): """ Compute the digest of the file. Returns the hex digest of the file; to retrieve the digest in other forms, pass an explicit hasher or tuple of hashers. :param path: An optional path to a subelement of this directory. :param hasher: The string name of the desired hash algorithm, or a digester object as returned by one of the hashers present in ``hashlib``, or a tuple of such objects. If not given, defaults to ``utils.DEFAULT_HASHER``. :returns: The digest of the file, in hex. If a tuple of hashers was passed for ``hasher``, then the first hasher will be returned. """ # Set up the hasher, first if not hasher: raise ValueError('a hasher must be specified') elif isinstance(hasher, six.string_types): hasher = (utils.get_hasher(hasher)(),) elif not isinstance(hasher, tuple): hasher = (hasher,) # Open the desired file and digest it with self.open(path) as f: return utils.digest(f, hasher)
def test_basic(self): fo = io.BytesIO(six.b("12345678901234")) digesters = [ mock.Mock(**{'hexdigest.return_value': 'd16e57'}), mock.Mock(), mock.Mock(), ] result = utils.digest(fo, digesters) self.assertEqual(result, 'd16e57') for i, digester in enumerate(digesters): digester.update.assert_has_calls([ mock.call(six.b('1234')), mock.call(six.b('5678')), mock.call(six.b('9012')), mock.call(six.b('34')), ]) self.assertEqual(digester.update.call_count, 4) if i == 0: digester.hexdigest.assert_called_once_with() else: self.assertFalse(digester.hexdigest.called)
def tar(self, filename, start=os.curdir, compression=utils.unset, hasher=None): """ Create a tar file with the given filename. :param filename: The filename of the tar file to create. If ``compression`` is not given, it will be inferred from the filename. The appropriate extensions will be added to the filename, if necessary. If a compression extension on the filename does not match the specified compression, a ``ValueError`` will be raised. :param start: The directory from which to start the tar process. If not given, starts from the current directory and includes all files in the directory. If it is a parent of the current directory, only the current directory will be included in the tarball. A ``ValueError`` will be raised if the tar process cannot start from the given location. :param compression: If given, specifies the compression to use. The ``filename`` will be modified to include the appropriate extension. A ``ValueError`` will be raised if the given compression is not supported or if a compression was inferred from the filename. :param hasher: If given, requests that a hash of the resulting tar file be computed. May be a ``True`` value to use the default hasher; a string to specify a hasher; or a tuple of hashers. :returns: The final filename that was created. If ``hasher`` was specified, a tuple will be returned, with the second element consisting of the hex digest of the tar file. """ # If the filename is a FSEntry, use its path if isinstance(filename, FSEntry): filename = filename.path # Parse the file name and set the compression filename = tarname.TarFileName(utils.abspath(filename, cwd=self.path)) if compression is not utils.unset: filename.compression = compression # Determine the starting location and file list start = self._rel(start, False) filelist = None rel_path = utils.RelPath(start, self.name) if rel_path.parents and rel_path.remainder: raise ValueError("cannot start tar-ing from '%s'" % rel_path) elif not rel_path.parents and not rel_path.remainder: start = self.path elif rel_path.parents: start = os.path.normpath( os.path.join(self.path, [os.pardir] * rel_path.parents)) filelist = [os.path.join(*rel_path.path_list[-rel_path.parents:])] start = self.tree._full(start) if filelist is None: filelist = os.listdir(start) # OK, let's build the tarball tar = tarfile.open(str(filename), 'w:%s' % filename.compression or '') try: with utils.workdir(start): for fname in filelist: try: tar.add(fname) except Exception: pass finally: tar.close() # Begin building the result result = str(filename) # If a hash was requested, generate it if hasher: # Select the hasher(s) if hasher is True: hasher = (utils.get_hasher(utils.DEFAULT_HASHER)(),) elif isinstance(hasher, six.string_types): hasher = (utils.get_hasher(hasher)(),) elif not isinstance(hasher, tuple): hasher = (hasher,) # Open the file with open(result) as f: result = (result, utils.digest(f, hasher)) return result