0
# Buffer Management
1
2
Efficient memory and file-based buffer system for handling request and response data with automatic overflow management and streaming capabilities.
3
4
## Capabilities
5
6
### Buffer Base Class
7
8
Foundation class for all buffer implementations providing common interface and operations.
9
10
```python { .api }
11
class FileBasedBuffer:
12
"""
13
Base class for file-like buffer implementations.
14
15
Provides common buffering operations with support for reading,
16
writing, seeking, and efficient data management across different
17
storage backends (memory, temporary files).
18
"""
19
20
remain: int = 0 # Number of bytes remaining to be read
21
22
def __init__(self, file, from_buffer=None):
23
"""
24
Initialize buffer with file-like object.
25
26
Parameters:
27
- file: File-like object for data storage
28
- from_buffer: Optional existing buffer to copy data from
29
"""
30
31
def __len__(self):
32
"""
33
Get number of bytes remaining in buffer.
34
35
Returns:
36
int: Number of unread bytes
37
"""
38
39
def __bool__(self):
40
"""
41
Check if buffer exists (always True for FileBasedBuffer).
42
43
Returns:
44
bool: Always True
45
"""
46
47
def append(self, s):
48
"""
49
Append data to buffer.
50
51
Parameters:
52
- s (bytes): Data to append to buffer
53
54
Notes:
55
- Maintains current read position
56
- Updates remaining byte count
57
"""
58
59
def get(self, numbytes=-1, skip=False):
60
"""
61
Read data from buffer.
62
63
Parameters:
64
- numbytes (int): Number of bytes to read (-1 for all)
65
- skip (bool): Whether to advance read position
66
67
Returns:
68
bytes: Read data
69
70
Notes:
71
- If skip=False, read position unchanged
72
- If skip=True, data is consumed from buffer
73
"""
74
75
def skip(self, numbytes, allow_prune=0):
76
"""
77
Skip bytes in buffer without reading.
78
79
Parameters:
80
- numbytes (int): Number of bytes to skip
81
- allow_prune (int): Unused parameter for compatibility
82
83
Raises:
84
ValueError: If trying to skip more bytes than available
85
"""
86
87
def newfile(self):
88
"""
89
Create new file-like object for buffer.
90
91
Returns:
92
File-like object
93
94
Notes:
95
- Must be implemented by subclasses
96
- Used during buffer pruning operations
97
"""
98
99
def prune(self):
100
"""
101
Remove consumed data to free memory/disk space.
102
103
Creates new file with only unread data, discarding
104
data that has already been consumed.
105
"""
106
107
def getfile(self):
108
"""
109
Get underlying file-like object.
110
111
Returns:
112
File-like object used for storage
113
"""
114
115
def close(self):
116
"""
117
Close buffer and release resources.
118
119
Closes underlying file if it has a close method
120
and resets remaining byte count to zero.
121
"""
122
```
123
124
### Memory-Based Buffer
125
126
In-memory buffer implementation using BytesIO for small to medium sized data.
127
128
```python { .api }
129
class BytesIOBasedBuffer(FileBasedBuffer):
130
"""
131
Memory-based buffer using BytesIO for storage.
132
133
Efficient for small to medium sized data that fits
134
comfortably in memory. Faster than file-based alternatives
135
but limited by available RAM.
136
"""
137
138
def __init__(self, from_buffer=None):
139
"""
140
Initialize memory-based buffer.
141
142
Parameters:
143
- from_buffer: Optional existing buffer to copy data from
144
145
Notes:
146
- Uses BytesIO for in-memory storage
147
- No file system involvement
148
"""
149
150
def newfile(self):
151
"""
152
Create new BytesIO object.
153
154
Returns:
155
BytesIO: New in-memory file-like object
156
"""
157
```
158
159
### Temporary File Buffer
160
161
File-based buffer using temporary files for large data that exceeds memory limits.
162
163
```python { .api }
164
class TempfileBasedBuffer(FileBasedBuffer):
165
"""
166
File-based buffer using temporary files for storage.
167
168
Automatically handles large data by storing it in temporary
169
files, providing unlimited capacity limited only by disk space.
170
Files are automatically cleaned up when buffer is closed.
171
"""
172
173
def __init__(self, from_buffer=None):
174
"""
175
Initialize temporary file-based buffer.
176
177
Parameters:
178
- from_buffer: Optional existing buffer to copy data from
179
180
Notes:
181
- Creates temporary file for storage
182
- File is automatically deleted when closed
183
"""
184
185
def newfile(self):
186
"""
187
Create new temporary file.
188
189
Returns:
190
TemporaryFile: New temporary file object
191
192
Notes:
193
- File created in binary mode ("w+b")
194
- Automatically deleted when closed
195
"""
196
```
197
198
### Read-Only File Buffer
199
200
Specialized buffer for serving static files efficiently with WSGI file_wrapper support.
201
202
```python { .api }
203
class ReadOnlyFileBasedBuffer(FileBasedBuffer):
204
"""
205
Read-only buffer for serving existing files efficiently.
206
207
Optimized for serving static files as WSGI responses with
208
support for range requests and efficient streaming. Used
209
as wsgi.file_wrapper for optimal file serving performance.
210
"""
211
212
def __init__(self, file, block_size=32768):
213
"""
214
Initialize read-only file buffer.
215
216
Parameters:
217
- file: File-like object to read from
218
- block_size (int): Block size for iteration (default: 32KB)
219
220
Notes:
221
- File must be opened in binary read mode
222
- Supports seekable and non-seekable files
223
"""
224
225
block_size: int # Block size for iteration
226
seekable: callable # Seekable check function (if available)
227
228
def __iter__(self):
229
"""
230
Iterate over file contents in blocks.
231
232
Yields:
233
bytes: File data in block_size chunks
234
235
Notes:
236
- Efficient for WSGI response streaming
237
- Automatically handles file positioning
238
"""
239
240
def __next__(self):
241
"""
242
Get next block of file data.
243
244
Returns:
245
bytes: Next block of data
246
247
Raises:
248
StopIteration: When end of file reached
249
"""
250
251
def close(self):
252
"""
253
Close the underlying file.
254
255
Notes:
256
- Only closes if file has close method
257
- Safe to call multiple times
258
"""
259
```
260
261
### Overflowable Buffer
262
263
Advanced buffer that automatically switches between memory and file storage based on size.
264
265
```python { .api }
266
class OverflowableBuffer:
267
"""
268
Buffer that overflows from memory to temporary files.
269
270
Starts with in-memory storage for efficiency and automatically
271
switches to temporary file storage when data exceeds the
272
configured overflow threshold.
273
"""
274
275
def __init__(self, overflow_threshold):
276
"""
277
Initialize overflowable buffer.
278
279
Parameters:
280
- overflow_threshold (int): Byte limit before switching to file
281
282
Notes:
283
- Starts with BytesIOBasedBuffer
284
- Switches to TempfileBasedBuffer when threshold exceeded
285
"""
286
287
def append(self, data):
288
"""
289
Append data with automatic overflow handling.
290
291
Parameters:
292
- data (bytes): Data to append
293
294
Notes:
295
- Automatically switches to file storage if needed
296
- Transparent to caller after overflow
297
"""
298
299
def get(self, numbytes=-1, skip=False):
300
"""
301
Read data from buffer (delegates to underlying buffer).
302
303
Parameters:
304
- numbytes (int): Number of bytes to read
305
- skip (bool): Whether to consume data
306
307
Returns:
308
bytes: Read data
309
"""
310
311
def __len__(self):
312
"""Get number of bytes in buffer."""
313
314
def close(self):
315
"""Close buffer and clean up resources."""
316
```
317
318
### Buffer Constants
319
320
Important constants controlling buffer behavior and performance characteristics.
321
322
```python { .api }
323
# Buffer copy operations
324
COPY_BYTES: int = 262144 # 256KB - chunk size for copying data between buffers
325
326
# String buffer limits
327
STRBUF_LIMIT: int = 8192 # 8KB - maximum size for simple string buffers
328
```
329
330
### Buffer Usage Examples
331
332
Common patterns for working with waitress buffer system.
333
334
#### Basic Buffer Operations
335
336
```python
337
from waitress.buffers import BytesIOBasedBuffer, TempfileBasedBuffer
338
339
# Memory-based buffer for small data
340
memory_buffer = BytesIOBasedBuffer()
341
memory_buffer.append(b"Hello, ")
342
memory_buffer.append(b"World!")
343
344
# Read data without consuming
345
data = memory_buffer.get()
346
print(data) # b"Hello, World!"
347
print(len(memory_buffer)) # 13
348
349
# Read and consume data
350
consumed = memory_buffer.get(5, skip=True)
351
print(consumed) # b"Hello"
352
print(len(memory_buffer)) # 8
353
354
# File-based buffer for large data
355
file_buffer = TempfileBasedBuffer()
356
large_data = b"x" * 1000000 # 1MB of data
357
file_buffer.append(large_data)
358
print(len(file_buffer)) # 1000000
359
```
360
361
#### Overflowable Buffer Usage
362
363
```python
364
from waitress.buffers import OverflowableBuffer
365
366
# Buffer that overflows at 64KB
367
buffer = OverflowableBuffer(65536)
368
369
# Add small amounts of data (stays in memory)
370
for i in range(1000):
371
buffer.append(f"Line {i}\n".encode())
372
373
# Add large amount that triggers overflow to file
374
large_chunk = b"x" * 100000
375
buffer.append(large_chunk) # Now using temporary file
376
377
# Usage is transparent after overflow
378
data = buffer.get(1000)
379
print(len(data)) # 1000 bytes
380
```
381
382
#### File Serving with ReadOnlyFileBuffer
383
384
```python
385
from waitress.buffers import ReadOnlyFileBasedBuffer
386
387
def serve_file_app(environ, start_response):
388
"""WSGI app that serves files efficiently."""
389
390
file_path = "/path/to/large/file.dat"
391
392
try:
393
with open(file_path, 'rb') as f:
394
# Create read-only buffer for efficient serving
395
file_buffer = ReadOnlyFileBasedBuffer(f, block_size=65536)
396
397
status = '200 OK'
398
headers = [
399
('Content-Type', 'application/octet-stream'),
400
('Content-Length', str(os.path.getsize(file_path)))
401
]
402
start_response(status, headers)
403
404
# Return buffer for efficient streaming
405
return file_buffer
406
407
except FileNotFoundError:
408
status = '404 Not Found'
409
headers = [('Content-Type', 'text/plain')]
410
start_response(status, headers)
411
return [b'File not found']
412
413
# The WSGI server will iterate over the buffer efficiently
414
```
415
416
#### Buffer Copying and Management
417
418
```python
419
from waitress.buffers import BytesIOBasedBuffer, TempfileBasedBuffer
420
421
# Create source buffer with data
422
source = BytesIOBasedBuffer()
423
source.append(b"Important data that needs to be copied")
424
425
# Create destination buffer from source
426
destination = TempfileBasedBuffer(from_buffer=source)
427
428
# Both buffers now contain the same data but are independent
429
source_data = source.get()
430
dest_data = destination.get()
431
assert source_data == dest_data
432
433
# Modifications to one don't affect the other
434
source.append(b" - modified")
435
assert source.get() != destination.get()
436
437
# Clean up
438
source.close()
439
destination.close()
440
```
441
442
### Performance Considerations
443
444
Guidelines for optimal buffer usage in different scenarios.
445
446
```python
447
# Buffer selection guidelines:
448
SMALL_DATA = "Use BytesIOBasedBuffer (< 8KB)" # Fastest, in-memory
449
MEDIUM_DATA = "Use OverflowableBuffer (8KB-1MB)" # Adaptive
450
LARGE_DATA = "Use TempfileBasedBuffer (> 1MB)" # File-based
451
STATIC_FILES = "Use ReadOnlyFileBasedBuffer" # Optimized serving
452
453
# Memory usage patterns:
454
MEMORY_EFFICIENT = "Use file-based buffers for large data"
455
SPEED_OPTIMIZED = "Use memory buffers for frequently accessed data"
456
BALANCED = "Use OverflowableBuffer for variable-size data"
457
458
# Buffer lifecycle:
459
CLEANUP_IMPORTANT = "Always call close() on file-based buffers"
460
TEMPORARY_FILES = "Automatically cleaned up when closed"
461
MEMORY_BUFFERS = "Cleaned up by garbage collector"
462
```
463
464
### Integration with HTTP Processing
465
466
How buffers integrate with waitress HTTP processing pipeline.
467
468
```python
469
# Request body buffering:
470
REQUEST_SMALL = "Stored in BytesIOBasedBuffer initially"
471
REQUEST_LARGE = "Overflows to TempfileBasedBuffer automatically"
472
REQUEST_STREAMING = "Processed incrementally as data arrives"
473
474
# Response buffering:
475
RESPONSE_HEADERS = "Buffered in memory for efficiency"
476
RESPONSE_BODY = "Uses appropriate buffer based on size"
477
RESPONSE_FILES = "Uses ReadOnlyFileBasedBuffer for static files"
478
479
# Connection management:
480
KEEP_ALIVE = "Buffers reused across requests on same connection"
481
PIPELINING = "Multiple request buffers managed per connection"
482
CLEANUP = "Buffers automatically cleaned up on connection close"
483
```