~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/__init__.py

MergeĀ fromĀ mainline

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
"""
21
21
 
22
22
import errno
 
23
from collections import deque
23
24
from copy import deepcopy
24
25
from stat import *
25
26
import sys
165
166
                        as a single parameter.
166
167
        """
167
168
        total = self._get_total(multi)
 
169
        result = []
168
170
        count = 0
169
171
        for entry in multi:
170
172
            self._update_pb(pb, msg, count, total)
171
173
            if expand:
172
 
                func(*entry)
 
174
                result.append(func(*entry))
173
175
            else:
174
 
                func(entry)
 
176
                result.append(func(entry))
175
177
            count += 1
176
 
        return count
 
178
        return tuple(result)
177
179
 
178
180
    def abspath(self, relpath):
179
181
        """Return the full url to the given relative path.
203
205
        """Does the file relpath exist?
204
206
        
205
207
        Note that some transports MAY allow querying on directories, but this
206
 
        is not part of the protocol.
 
208
        is not part of the protocol.  In other words, the results of 
 
209
        t.has("a_directory_name") are undefined."
207
210
        """
208
211
        raise NotImplementedError
209
212
 
243
246
        """
244
247
        raise NotImplementedError
245
248
 
 
249
    def readv(self, relpath, offsets):
 
250
        """Get parts of the file at the given relative path.
 
251
 
 
252
        :offsets: A list of (offset, size) tuples.
 
253
        :return: A list or generator of (offset, data) tuples
 
254
        """
 
255
        def do_combined_read(combined_offsets):
 
256
            total_size = 0
 
257
            for offset, size in combined_offsets:
 
258
                total_size += size
 
259
            mutter('readv coalesced %d reads.', len(combined_offsets))
 
260
            offset = combined_offsets[0][0]
 
261
            fp.seek(offset)
 
262
            data = fp.read(total_size)
 
263
            pos = 0
 
264
            for offset, size in combined_offsets:
 
265
                yield offset, data[pos:pos + size]
 
266
                pos += size
 
267
 
 
268
        if not len(offsets):
 
269
            return
 
270
        fp = self.get(relpath)
 
271
        pending_offsets = deque(offsets)
 
272
        combined_offsets = []
 
273
        while len(pending_offsets):
 
274
            offset, size = pending_offsets.popleft()
 
275
            if not combined_offsets:
 
276
                combined_offsets = [[offset, size]]
 
277
            else:
 
278
                if (len (combined_offsets) < 50 and
 
279
                    combined_offsets[-1][0] + combined_offsets[-1][1] == offset):
 
280
                    # combatible offset:
 
281
                    combined_offsets.append([offset, size])
 
282
                else:
 
283
                    # incompatible, or over the threshold issue a read and yield
 
284
                    pending_offsets.appendleft((offset, size))
 
285
                    for result in do_combined_read(combined_offsets):
 
286
                        yield result
 
287
                    combined_offsets = []
 
288
        # whatever is left is a single coalesced request
 
289
        if len(combined_offsets):
 
290
            for result in do_combined_read(combined_offsets):
 
291
                yield result
 
292
 
246
293
    def get_multi(self, relpaths, pb=None):
247
294
        """Get a list of file-like objects, one for each entry in relpaths.
248
295
 
280
327
        """
281
328
        def put(path, f):
282
329
            self.put(path, f, mode=mode)
283
 
        return self._iterate_over(files, put, pb, 'put', expand=True)
 
330
        return len(self._iterate_over(files, put, pb, 'put', expand=True))
284
331
 
285
332
    def mkdir(self, relpath, mode=None):
286
333
        """Create a directory at the given path."""
290
337
        """Create a group of directories"""
291
338
        def mkdir(path):
292
339
            self.mkdir(path, mode=mode)
293
 
        return self._iterate_over(relpaths, mkdir, pb, 'mkdir', expand=False)
 
340
        return len(self._iterate_over(relpaths, mkdir, pb, 'mkdir', expand=False))
294
341
 
295
342
    def append(self, relpath, f):
296
343
        """Append the text in the file-like or string object to 
297
344
        the supplied location.
 
345
 
 
346
        returns the length of f before the content was written to it.
298
347
        """
299
348
        raise NotImplementedError
300
349
 
336
385
        def copy_entry(path):
337
386
            other.put(path, self.get(path), mode=mode)
338
387
 
339
 
        return self._iterate_over(relpaths, copy_entry, pb, 'copy_to', expand=False)
 
388
        return len(self._iterate_over(relpaths, copy_entry, pb, 'copy_to', expand=False))
340
389
 
341
390
    def copy_tree(self, from_relpath, to_relpath):
342
391
        """Copy a subtree from one relpath to another.
627
676
                # from running this test
628
677
                pass
629
678
        return result
 
679
 
 
680
 
 
681
class TransportLogger(object):
 
682
    """Adapt a transport to get clear logging data on api calls.
 
683
    
 
684
    Feel free to extend to log whatever calls are of interest.
 
685
    """
 
686
 
 
687
    def __init__(self, adapted):
 
688
        self._adapted = adapted
 
689
        self._calls = []
 
690
 
 
691
    def get(self, name):
 
692
        self._calls.append((name,))
 
693
        return self._adapted.get(name)
 
694
 
 
695
    def __getattr__(self, name):
 
696
        """Thunk all undefined access through to self._adapted."""
 
697
        # raise AttributeError, name 
 
698
        return getattr(self._adapted, name)
 
699
 
 
700
    def readv(self, name, offsets):
 
701
        self._calls.append((name, offsets))
 
702
        return self._adapted.readv(name, offsets)
630
703
        
631
704
 
632
705
# None is the default transport, for things with no url scheme