165
166
as a single parameter.
167
168
total = self._get_total(multi)
169
171
for entry in multi:
170
172
self._update_pb(pb, msg, count, total)
174
result.append(func(*entry))
176
result.append(func(entry))
178
180
def abspath(self, relpath):
179
181
"""Return the full url to the given relative path.
203
205
"""Does the file relpath exist?
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."
208
211
raise NotImplementedError
244
247
raise NotImplementedError
249
def readv(self, relpath, offsets):
250
"""Get parts of the file at the given relative path.
252
:offsets: A list of (offset, size) tuples.
253
:return: A list or generator of (offset, data) tuples
255
def do_combined_read(combined_offsets):
257
for offset, size in combined_offsets:
259
mutter('readv coalesced %d reads.', len(combined_offsets))
260
offset = combined_offsets[0][0]
262
data = fp.read(total_size)
264
for offset, size in combined_offsets:
265
yield offset, data[pos:pos + size]
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]]
278
if (len (combined_offsets) < 50 and
279
combined_offsets[-1][0] + combined_offsets[-1][1] == offset):
281
combined_offsets.append([offset, size])
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):
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):
246
293
def get_multi(self, relpaths, pb=None):
247
294
"""Get a list of file-like objects, one for each entry in relpaths.
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))
285
332
def mkdir(self, relpath, mode=None):
286
333
"""Create a directory at the given path."""
290
337
"""Create a group of directories"""
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))
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.
346
returns the length of f before the content was written to it.
299
348
raise NotImplementedError
336
385
def copy_entry(path):
337
386
other.put(path, self.get(path), mode=mode)
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))
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
681
class TransportLogger(object):
682
"""Adapt a transport to get clear logging data on api calls.
684
Feel free to extend to log whatever calls are of interest.
687
def __init__(self, adapted):
688
self._adapted = adapted
692
self._calls.append((name,))
693
return self._adapted.get(name)
695
def __getattr__(self, name):
696
"""Thunk all undefined access through to self._adapted."""
697
# raise AttributeError, name
698
return getattr(self._adapted, name)
700
def readv(self, name, offsets):
701
self._calls.append((name, offsets))
702
return self._adapted.readv(name, offsets)
632
705
# None is the default transport, for things with no url scheme