13
13
14
14
15
15
class RangeResponse :
16
- tail_mark = 0
16
+ """
17
+ Adapted from `obskyr's ResponseStream demo code
18
+ <https://gist.github.com/obskyr/b9d4b4223e7eaf4eedcd9defabb34f13>`_,
19
+ this class handles the streamed partial request as a file-like object.
20
+ """
21
+
22
+ tail_mark : int = 0
23
+ """
24
+ The amount by which to shorten the 'tail' (i.e. the upper end) of the
25
+ range when deciding if it is 'consumed'. Incremented within the
26
+ :meth:`~range_streams.range_stream.RangeStream.handle_overlap` method
27
+ when the ``pruning_level`` is set to ``1`` (indicating a "replant" policy).
28
+
29
+ Under a 'replant' policy, when a new range is to be added and would overlap
30
+ at the tail of an existing range, the pre-existing range should be effectively
31
+ truncated by 'marking their tails'
32
+ (where `an existing range` is assumed here to only be considered a range
33
+ if it is not 'consumed' yet).
34
+ """
17
35
18
36
def __init__ (
19
37
self ,
@@ -43,10 +61,18 @@ def client(self):
43
61
44
62
@property
45
63
def url (self ) -> str :
64
+ """
65
+ A wrapper to access the :attr:`~range_streams.range_stream.RangeStream.url`
66
+ of the 'parent' :class:`~range_streams.range_stream.RangeStream`.
67
+ """
46
68
return self .parent_stream .url
47
69
48
70
@property
49
71
def name (self ) -> str :
72
+ """
73
+ A wrapper to access the :attr:`~range_streams.range_stream.RangeStream.name`
74
+ of the 'parent' :class:`~range_streams.range_stream.RangeStream`.
75
+ """
50
76
return self .parent_stream .name
51
77
52
78
def _load_all (self ):
@@ -63,9 +89,15 @@ def _load_until(self, goal_position):
63
89
break
64
90
65
91
def tell (self ):
92
+ """
93
+ File-like tell (position indicator) within the range request stream.
94
+ """
66
95
return self ._bytes .tell ()
67
96
68
97
def read (self , size = None ):
98
+ """
99
+ File-like reading within the range request stream.
100
+ """
69
101
left_off_at = self ._bytes .tell ()
70
102
if size is None :
71
103
self ._load_all ()
@@ -77,11 +109,39 @@ def read(self, size=None):
77
109
return self ._bytes .read (size )
78
110
79
111
def seek (self , position , whence = SEEK_SET ):
112
+ """
113
+ File-like seeking within the range request stream.
114
+ """
80
115
if whence == SEEK_END :
81
116
self ._load_all ()
82
117
self ._bytes .seek (position , whence )
83
118
84
119
def is_consumed (self ) -> bool :
120
+ """
121
+ Whether the :meth:`~range_streams.range_response.RangeResponse.tell`
122
+ position (indicating 'consumed' or 'read so far') along with the
123
+ :attr:`~range_streams.range_response.RangeResponse.tail_mark` indicates
124
+ whether the stream should be considered consumed.
125
+
126
+ The :attr:`~range_streams.range_response.RangeResponse.tail_mark`
127
+ is part of a mechanism to 'shorten' ranges when an overlap is detected,
128
+ to preserve the one-to-one integrity of the :class:`~ranges.RangeDict`
129
+ (see notes on the "replant" policy of
130
+ :meth:`~range_streams.range_stream.RangeStream.handle_overlap`, set
131
+ by the ``pruning_level`` passed into
132
+ :class:`~range_streams.range_stream.RangeStream` on initialisation).
133
+
134
+ Note that there is (absolutely!) nothing stopping a stream from being
135
+ re-consumed, but this library works on the assumption that all streams
136
+ will be handled in an efficient manner (with any data read out from them
137
+ either used once only or else will be reused from the first output rather
138
+ than re-accessed directly from the stream itself).
139
+
140
+ To this end, :class:`~range_streams.range_stream.RangeStream` has measures
141
+ in place to "decommission" ranges once they are consumed (see in particular
142
+ :meth:`~range_streams.range_stream.RangeStream.burn_range` and
143
+ :meth:`~range_streams.range_stream.RangeStream.handle_overlap`).
144
+ """
85
145
read_so_far = self .tell ()
86
146
len_to_read = range_len (self .request .range ) - self .tail_mark
87
147
return read_so_far - len_to_read > 0
0 commit comments