287
287
# where the biggest benefit between combining reads and
288
288
# and seeking is. Consider a runtime auto-tune.
289
289
_bytes_to_read_before_seek = 0
290
# By default, we won't force .close() to be synchronous for remote readv.
291
# (SFTP in particular has an async .close() call.) However, when running
292
# the test suite, it would be really nice to have them be synchronous, to
294
_synchronous_close = False
291
296
def __init__(self, base):
292
297
super(Transport, self).__init__()
673
678
This uses _coalesce_offsets to issue larger reads and fewer seeks.
675
:param fp: A file-like object that supports seek() and read(size)
680
:param fp: A file-like object that supports seek() and read(size).
681
Note that implementations are allowed to call .close() on this file
682
handle, so don't trust that you can use it for other work.
676
683
:param offsets: A list of offsets to be read from the given file.
677
684
:return: yield (pos, data) tuples for each request
690
697
# Cache the results, but only until they have been fulfilled
692
for c_offset in coalesced:
693
# TODO: jam 20060724 it might be faster to not issue seek if
694
# we are already at the right location. This should be
696
fp.seek(c_offset.start)
697
data = fp.read(c_offset.length)
698
if len(data) < c_offset.length:
699
raise errors.ShortReadvError(relpath, c_offset.start,
700
c_offset.length, actual=len(data))
701
for suboffset, subsize in c_offset.ranges:
702
key = (c_offset.start+suboffset, subsize)
703
data_map[key] = data[suboffset:suboffset+subsize]
700
for c_offset in coalesced:
701
# TODO: jam 20060724 it might be faster to not issue seek if
702
# we are already at the right location. This should be
704
fp.seek(c_offset.start)
705
data = fp.read(c_offset.length)
706
if len(data) < c_offset.length:
707
raise errors.ShortReadvError(relpath, c_offset.start,
708
c_offset.length, actual=len(data))
709
for suboffset, subsize in c_offset.ranges:
710
key = (c_offset.start+suboffset, subsize)
711
data_map[key] = data[suboffset:suboffset+subsize]
705
# Now that we've read some data, see if we can yield anything back
706
while cur_offset_and_size in data_map:
707
this_data = data_map.pop(cur_offset_and_size)
708
this_offset = cur_offset_and_size[0]
710
cur_offset_and_size = offset_stack.next()
711
except StopIteration:
712
# Close the file handle as there will be no more data
713
# The handle would normally be cleaned up as this code goes
714
# out of scope, but as we are a generator, not all code
715
# will re-enter once we have consumed all the expected
717
# zip(range(len(requests)), readv(foo, requests))
718
# Will stop because the range is done, and not run the
719
# cleanup code for the readv().
721
cur_offset_and_size = None
722
yield this_offset, this_data
713
# Now that we've read some data, see if we can yield anything back
714
while cur_offset_and_size in data_map:
715
this_data = data_map.pop(cur_offset_and_size)
716
this_offset = cur_offset_and_size[0]
718
cur_offset_and_size = offset_stack.next()
719
except StopIteration:
720
cur_offset_and_size = None
721
yield this_offset, this_data
723
# Note that this is not python2.4 compatible as try/finally in
724
# generators was added in python 2.5
724
727
def _sort_expand_and_combine(self, offsets, upper_limit):
725
728
"""Helper for readv.