1#!/usr/bin/env python 2# 3# Copyright 2015 Google Inc. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Small helper class to provide a small slice of a stream. 18 19This class reads ahead to detect if we are at the end of the stream. 20""" 21 22from apitools.base.py import exceptions 23 24 25# TODO(user): Consider replacing this with a StringIO. 26class BufferedStream(object): 27 28 """Buffers a stream, reading ahead to determine if we're at the end.""" 29 30 def __init__(self, stream, start, size): 31 self.__stream = stream 32 self.__start_pos = start 33 self.__buffer_pos = 0 34 self.__buffered_data = self.__stream.read(size) 35 self.__stream_at_end = len(self.__buffered_data) < size 36 self.__end_pos = self.__start_pos + len(self.__buffered_data) 37 38 def __str__(self): 39 return ('Buffered stream %s from position %s-%s with %s ' 40 'bytes remaining' % (self.__stream, self.__start_pos, 41 self.__end_pos, self._bytes_remaining)) 42 43 def __len__(self): 44 return len(self.__buffered_data) 45 46 @property 47 def stream_exhausted(self): 48 return self.__stream_at_end 49 50 @property 51 def stream_end_position(self): 52 return self.__end_pos 53 54 @property 55 def _bytes_remaining(self): 56 return len(self.__buffered_data) - self.__buffer_pos 57 58 def read(self, size=None): # pylint: disable=invalid-name 59 """Reads from the buffer.""" 60 if size is None or size < 0: 61 raise exceptions.NotYetImplementedError( 62 'Illegal read of size %s requested on BufferedStream. ' 63 'Wrapped stream %s is at position %s-%s, ' 64 '%s bytes remaining.' % 65 (size, self.__stream, self.__start_pos, self.__end_pos, 66 self._bytes_remaining)) 67 68 data = '' 69 if self._bytes_remaining: 70 size = min(size, self._bytes_remaining) 71 data = self.__buffered_data[ 72 self.__buffer_pos:self.__buffer_pos + size] 73 self.__buffer_pos += size 74 return data 75