move compression functions to module dedup.compression
[~helmut/debian-dedup.git] / dedup / hashing.py
1 class HashBlacklist(object):
2     """Turn a hashlib-like object into a hash that returns None for some
3     blacklisted hashes instead of the real hash value.
4
5     We only work with hexdigests here, so diget() disappears. The methods
6     copy and update as well as the name attribute keep working as expected.
7     """
8     def __init__(self, hashobj, blacklist=()):
9         """
10         @param hashobj: a hashlib-like object
11         @param blacklist: an object providing __contains__.
12             hexdigest values which are contained in the blacklist
13             are turned into None values
14         """
15         self.hashobj = hashobj
16         self.blacklist = blacklist
17         self.update = self.hashobj.update
18
19     @property
20     def name(self):
21         return self.hashobj.name
22
23     def hexdigest(self):
24         digest = self.hashobj.hexdigest()
25         if digest in self.blacklist:
26             return None
27         return digest
28
29     def copy(self):
30         return HashBlacklist(self.hashobj.copy(), self.blacklist)
31
32 class DecompressedHash(object):
33     """Apply a decompression function before the hash. This class provides the
34     hashlib interface (update, hexdigest, copy) excluding digest and name."""
35     def __init__(self, decompressor, hashobj):
36         """
37         @param decompressor: a decompression object like bz2.BZ2Decompressor or
38             lzma.LZMADecompressor. It has to provide methods decompress and
39             copy as well as an unused_data attribute. It may provide a flush
40             method.
41         @param hashobj: a hashlib-like obj providing methods update, hexdigest
42             and copy
43         """
44         self.decompressor = decompressor
45         self.hashobj = hashobj
46
47     def update(self, data):
48         self.hashobj.update(self.decompressor.decompress(data))
49
50     def hexdigest(self):
51         if not hasattr(self.decompressor, "flush"):
52             return self.hashobj.hexdigest()
53         tmpdecomp = self.decompressor.copy()
54         data = tmpdecomp.flush()
55         tmphash = self.hashobj.copy()
56         tmphash.update(data)
57         return tmphash.hexdigest()
58
59     def copy(self):
60         return DecompressedHash(self.decompressor.copy(), self.hashobj.copy())
61
62 class SuppressingHash(object):
63     """A hash that silences exceptions from the update and hexdigest methods of
64     a hashlib-like object. If an exception has occured, hexdigest always
65     returns None."""
66     def __init__(self, hashobj, exceptions=()):
67         """
68         @param hashobj: a hashlib-like object providing methods update, copy
69             and hexdigest. If a name attribute is present, it is mirrored as
70             well.
71         @type exceptions: tuple
72         @param exceptions: exception classes to be suppressed
73         """
74         self.hashobj = hashobj
75         self.exceptions = exceptions
76         if hasattr(hashobj, "name"):
77             self.name = hashobj.name
78
79     def update(self, data):
80         if self.hashobj:
81             try:
82                 self.hashobj.update(data)
83             except self.exceptions:
84                 self.hashobj = None
85
86     def hexdigest(self):
87         if self.hashobj:
88             try:
89                 return self.hashobj.hexdigest()
90             except self.exceptions:
91                 self.hashobj = None
92         return None
93
94     def copy(self):
95         if self.hashobj:
96             return SuppressingHash(self.hashobj.copy(), self.exceptions)
97         return SuppressingHash(None, self.exceptions)
98
99 def hash_file(hashobj, filelike, blocksize=65536):
100     """Feed the entire contents from the given filelike to the given hashobj.
101     @param hashobj: hashlib-like object providing an update method
102     @param filelike: file-like object providing read(size)
103     """
104     data = filelike.read(blocksize)
105     while data:
106         hashobj.update(data)
107         data = filelike.read(blocksize)
108     return hashobj