~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/hashcache.py

  • Committer: Martin Pool
  • Date: 2006-07-12 05:22:48 UTC
  • mto: This revision was merged to the branch mainline in revision 1856.
  • Revision ID: mbp@sourcefrog.net-20060712052248-789ab09214cf9fef
      Improvements to hashcache testing:
      
       - moves the functions that depend on the OS state into HashCache
       - removes dead 'FixThisError' (??) from test_hashcache
       - adds new FakeHashCache, which gives the test suite control of
         how the files and clock appear to the hashcache
       - new tests using this which check three key behaviours:
         - new files are not inserted in the cache
         - old files are inserted in the cache and can hit
         - if a file is modified within a short time window, we don't get
           an incorrect cached result

Show diffs side-by-side

added added

removed removed

Lines of Context:
42
42
FP_CTIME_COLUMN = 2
43
43
FP_MODE_COLUMN = 5
44
44
 
45
 
def _fingerprint(abspath):
46
 
    try:
47
 
        fs = os.lstat(abspath)
48
 
    except OSError:
49
 
        # might be missing, etc
50
 
        return None
51
 
 
52
 
    if stat.S_ISDIR(fs.st_mode):
53
 
        return None
54
 
 
55
 
    # we discard any high precision because it's not reliable; perhaps we
56
 
    # could do better on some systems?
57
 
    return (fs.st_size, long(fs.st_mtime),
58
 
            long(fs.st_ctime), fs.st_ino, fs.st_dev, fs.st_mode)
59
45
 
60
46
 
61
47
class HashCache(object):
131
117
        
132
118
        for inum, path, cache_entry in prep:
133
119
            abspath = pathjoin(self.root, path)
134
 
            fp = _fingerprint(abspath)
 
120
            fp = self._fingerprint(abspath)
135
121
            self.stat_count += 1
136
122
            
137
123
            cache_fp = cache_entry[1]
142
128
                self.needs_write = True
143
129
                del self._cache[path]
144
130
 
145
 
 
146
131
    def get_sha1(self, path):
147
132
        """Return the sha1 of a file.
148
133
        """
149
134
        abspath = pathjoin(self.root, path)
150
135
        self.stat_count += 1
151
 
        file_fp = _fingerprint(abspath)
 
136
        file_fp = self._fingerprint(abspath)
152
137
        
153
138
        if not file_fp:
154
139
            # not a regular file or not existing
173
158
 
174
159
        mode = file_fp[FP_MODE_COLUMN]
175
160
        if stat.S_ISREG(mode):
176
 
            digest = sha_file(file(abspath, 'rb', buffering=65000))
 
161
            digest = self._really_sha1_file(abspath)
177
162
        elif stat.S_ISLNK(mode):
178
163
            digest = sha.new(os.readlink(abspath)).hexdigest()
179
164
        else:
181
166
 
182
167
        # window of 3 seconds to allow for 2s resolution on windows,
183
168
        # unsynchronized file servers, etc.
184
 
        cutoff = int(time.time()) - 3
 
169
        cutoff = self._cutoff_time()
185
170
        if file_fp[FP_MTIME_COLUMN] >= cutoff \
186
171
                or file_fp[FP_CTIME_COLUMN] >= cutoff:
187
172
            # changed too recently; can't be cached.  we can
209
194
            self.needs_write = True
210
195
            self._cache[path] = (digest, file_fp)
211
196
        return digest
 
197
 
 
198
    def _really_sha1_file(self, abspath):
 
199
        """Calculate the SHA1 of a file by reading the full text"""
 
200
        return sha_file(file(abspath, 'rb', buffering=65000))
212
201
        
213
202
    def write(self):
214
203
        """Write contents of cache to file."""
252
241
            self.needs_write = True
253
242
            return
254
243
 
255
 
 
256
244
        hdr = inf.readline()
257
245
        if hdr != CACHE_HEADER:
258
246
            mutter('cache header marker not found at top of %s;'
283
271
            self._cache[path] = (sha1, fp)
284
272
 
285
273
        self.needs_write = False
 
274
 
 
275
    def _cutoff_time(self):
 
276
        """Return cutoff time.
 
277
 
 
278
        Files modified more recently than this time are at risk of being
 
279
        undetectably modified and so can't be cached.
 
280
        """
 
281
        return int(time.time()) - 3
286
282
           
287
 
 
288
 
 
289
 
        
 
283
    def _fingerprint(self, abspath):
 
284
        try:
 
285
            fs = os.lstat(abspath)
 
286
        except OSError:
 
287
            # might be missing, etc
 
288
            return None
 
289
        if stat.S_ISDIR(fs.st_mode):
 
290
            return None
 
291
        # we discard any high precision because it's not reliable; perhaps we
 
292
        # could do better on some systems?
 
293
        return (fs.st_size, long(fs.st_mtime),
 
294
                long(fs.st_ctime), fs.st_ino, fs.st_dev, fs.st_mode)