def _seek_to_next_range(self): # We will cross range boundaries if self._boundary is None: # If we don't have a boundary, we can't find another range raise errors.InvalidRange( self._path, self._pos, "Range (%s, %s) exhausted" % (self._start, self._size)) self.read_boundary() self.read_range_definition()
def read(self, size=-1): """Read size bytes from the current position in the file. Reading across ranges is not supported. We rely on the underlying http client to clean the socket if we leave bytes unread. This may occur for the final boundary line of a multipart response or for any range request not entirely consumed by the client (due to offset coalescing) :param size: The number of bytes to read. Leave unspecified or pass -1 to read to EOF. """ if (self._size > 0 and self._pos == self._start + self._size): if size == 0: return '' else: self._seek_to_next_range() elif self._pos < self._start: raise errors.InvalidRange( self._path, self._pos, "Can't read %s bytes before range (%s, %s)" % (size, self._start, self._size)) if self._size > 0: if size > 0 and self._pos + size > self._start + self._size: raise errors.InvalidRange( self._path, self._pos, "Can't read %s bytes across range (%s, %s)" % (size, self._start, self._size)) # read data from file buf = StringIO() limited = size if self._size > 0: # Don't read past the range definition limited = self._start + self._size - self._pos if size >= 0: limited = min(limited, size) osutils.pumpfile(self._file, buf, limited, self._max_read_size) data = buf.getvalue() # Update _pos respecting the data effectively read self._pos += len(data) return data
def seek(self, offset, whence=0): start_pos = self._pos if whence == 0: final_pos = offset elif whence == 1: final_pos = start_pos + offset elif whence == 2: if self._size > 0: final_pos = self._start + self._size + offset # offset < 0 else: raise errors.InvalidRange( self._path, self._pos, "RangeFile: can't seek from end while size is unknown") else: raise ValueError("Invalid value %s for whence." % whence) if final_pos < self._pos: # Can't seek backwards raise errors.InvalidRange( self._path, self._pos, 'RangeFile: trying to seek backwards to %s' % final_pos) if self._size > 0: cur_limit = self._start + self._size while final_pos > cur_limit: # We will cross range boundaries remain = cur_limit - self._pos if remain > 0: # Finish reading the current range self._checked_read(remain) self._seek_to_next_range() cur_limit = self._start + self._size size = final_pos - self._pos if size > 0: # size can be < 0 if we crossed a range boundary # We don't need the data, just read it and throw it away self._checked_read(size)
def test_invalid_range(self): error = errors.InvalidRange('path', 12, 'bad range') self.assertEquals("Invalid range access in path at 12: bad range", str(error))