71 lines
2.2 KiB
Python
71 lines
2.2 KiB
Python
|
"""Small helper class to provide a small slice of a stream."""
|
||
|
|
||
|
from six.moves import http_client
|
||
|
|
||
|
|
||
|
class StreamSlice(object):
|
||
|
"""Provides a slice-like object for streams.
|
||
|
|
||
|
:type stream: readable file-like object
|
||
|
:param stream: the stream to be buffered
|
||
|
|
||
|
:type max_bytes: integer
|
||
|
:param max_bytes: maximum number of bytes to return in the slice
|
||
|
"""
|
||
|
def __init__(self, stream, max_bytes):
|
||
|
self._stream = stream
|
||
|
self._remaining_bytes = max_bytes
|
||
|
self._max_bytes = max_bytes
|
||
|
|
||
|
def __repr__(self):
|
||
|
return 'Slice of stream %s with %s/%s bytes not yet read' % (
|
||
|
self._stream, self._remaining_bytes, self._max_bytes)
|
||
|
|
||
|
def __len__(self):
|
||
|
return self._max_bytes
|
||
|
|
||
|
def __nonzero__(self):
|
||
|
# For 32-bit python2.x, len() cannot exceed a 32-bit number; avoid
|
||
|
# accidental len() calls from httplib in the form of "if this_object:".
|
||
|
return bool(self._max_bytes)
|
||
|
|
||
|
@property
|
||
|
def length(self):
|
||
|
"""Maximum number of bytes to return in the slice.
|
||
|
|
||
|
.. note::
|
||
|
|
||
|
For 32-bit python2.x, len() cannot exceed a 32-bit number.
|
||
|
|
||
|
:rtype: integer
|
||
|
"""
|
||
|
return self._max_bytes
|
||
|
|
||
|
def read(self, size=None):
|
||
|
"""Read bytes from the slice.
|
||
|
|
||
|
Compared to other streams, there is one case where we may
|
||
|
unexpectedly raise an exception on read: if the underlying stream
|
||
|
is exhausted (i.e. returns no bytes on read), and the size of this
|
||
|
slice indicates we should still be able to read more bytes, we
|
||
|
raise :exc:`IncompleteRead`.
|
||
|
|
||
|
:type size: integer or None
|
||
|
:param size: If provided, read no more than size bytes from the stream.
|
||
|
|
||
|
:rtype: bytes
|
||
|
:returns: bytes read from this slice.
|
||
|
|
||
|
:raises: :exc:`IncompleteRead`
|
||
|
"""
|
||
|
if size is not None:
|
||
|
read_size = min(size, self._remaining_bytes)
|
||
|
else:
|
||
|
read_size = self._remaining_bytes
|
||
|
data = self._stream.read(read_size)
|
||
|
if read_size > 0 and not data:
|
||
|
raise http_client.IncompleteRead(
|
||
|
self._max_bytes - self._remaining_bytes, self._max_bytes)
|
||
|
self._remaining_bytes -= len(data)
|
||
|
return data
|