• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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