A production-quality pure-Python WSGI server with robust HTTP protocol support and comprehensive configuration options
—
Efficient memory and file-based buffer system for handling request and response data with automatic overflow management and streaming capabilities.
Foundation class for all buffer implementations providing common interface and operations.
class FileBasedBuffer:
"""
Base class for file-like buffer implementations.
Provides common buffering operations with support for reading,
writing, seeking, and efficient data management across different
storage backends (memory, temporary files).
"""
remain: int = 0 # Number of bytes remaining to be read
def __init__(self, file, from_buffer=None):
"""
Initialize buffer with file-like object.
Parameters:
- file: File-like object for data storage
- from_buffer: Optional existing buffer to copy data from
"""
def __len__(self):
"""
Get number of bytes remaining in buffer.
Returns:
int: Number of unread bytes
"""
def __bool__(self):
"""
Check if buffer exists (always True for FileBasedBuffer).
Returns:
bool: Always True
"""
def append(self, s):
"""
Append data to buffer.
Parameters:
- s (bytes): Data to append to buffer
Notes:
- Maintains current read position
- Updates remaining byte count
"""
def get(self, numbytes=-1, skip=False):
"""
Read data from buffer.
Parameters:
- numbytes (int): Number of bytes to read (-1 for all)
- skip (bool): Whether to advance read position
Returns:
bytes: Read data
Notes:
- If skip=False, read position unchanged
- If skip=True, data is consumed from buffer
"""
def skip(self, numbytes, allow_prune=0):
"""
Skip bytes in buffer without reading.
Parameters:
- numbytes (int): Number of bytes to skip
- allow_prune (int): Unused parameter for compatibility
Raises:
ValueError: If trying to skip more bytes than available
"""
def newfile(self):
"""
Create new file-like object for buffer.
Returns:
File-like object
Notes:
- Must be implemented by subclasses
- Used during buffer pruning operations
"""
def prune(self):
"""
Remove consumed data to free memory/disk space.
Creates new file with only unread data, discarding
data that has already been consumed.
"""
def getfile(self):
"""
Get underlying file-like object.
Returns:
File-like object used for storage
"""
def close(self):
"""
Close buffer and release resources.
Closes underlying file if it has a close method
and resets remaining byte count to zero.
"""In-memory buffer implementation using BytesIO for small to medium sized data.
class BytesIOBasedBuffer(FileBasedBuffer):
"""
Memory-based buffer using BytesIO for storage.
Efficient for small to medium sized data that fits
comfortably in memory. Faster than file-based alternatives
but limited by available RAM.
"""
def __init__(self, from_buffer=None):
"""
Initialize memory-based buffer.
Parameters:
- from_buffer: Optional existing buffer to copy data from
Notes:
- Uses BytesIO for in-memory storage
- No file system involvement
"""
def newfile(self):
"""
Create new BytesIO object.
Returns:
BytesIO: New in-memory file-like object
"""File-based buffer using temporary files for large data that exceeds memory limits.
class TempfileBasedBuffer(FileBasedBuffer):
"""
File-based buffer using temporary files for storage.
Automatically handles large data by storing it in temporary
files, providing unlimited capacity limited only by disk space.
Files are automatically cleaned up when buffer is closed.
"""
def __init__(self, from_buffer=None):
"""
Initialize temporary file-based buffer.
Parameters:
- from_buffer: Optional existing buffer to copy data from
Notes:
- Creates temporary file for storage
- File is automatically deleted when closed
"""
def newfile(self):
"""
Create new temporary file.
Returns:
TemporaryFile: New temporary file object
Notes:
- File created in binary mode ("w+b")
- Automatically deleted when closed
"""Specialized buffer for serving static files efficiently with WSGI file_wrapper support.
class ReadOnlyFileBasedBuffer(FileBasedBuffer):
"""
Read-only buffer for serving existing files efficiently.
Optimized for serving static files as WSGI responses with
support for range requests and efficient streaming. Used
as wsgi.file_wrapper for optimal file serving performance.
"""
def __init__(self, file, block_size=32768):
"""
Initialize read-only file buffer.
Parameters:
- file: File-like object to read from
- block_size (int): Block size for iteration (default: 32KB)
Notes:
- File must be opened in binary read mode
- Supports seekable and non-seekable files
"""
block_size: int # Block size for iteration
seekable: callable # Seekable check function (if available)
def __iter__(self):
"""
Iterate over file contents in blocks.
Yields:
bytes: File data in block_size chunks
Notes:
- Efficient for WSGI response streaming
- Automatically handles file positioning
"""
def __next__(self):
"""
Get next block of file data.
Returns:
bytes: Next block of data
Raises:
StopIteration: When end of file reached
"""
def close(self):
"""
Close the underlying file.
Notes:
- Only closes if file has close method
- Safe to call multiple times
"""Advanced buffer that automatically switches between memory and file storage based on size.
class OverflowableBuffer:
"""
Buffer that overflows from memory to temporary files.
Starts with in-memory storage for efficiency and automatically
switches to temporary file storage when data exceeds the
configured overflow threshold.
"""
def __init__(self, overflow_threshold):
"""
Initialize overflowable buffer.
Parameters:
- overflow_threshold (int): Byte limit before switching to file
Notes:
- Starts with BytesIOBasedBuffer
- Switches to TempfileBasedBuffer when threshold exceeded
"""
def append(self, data):
"""
Append data with automatic overflow handling.
Parameters:
- data (bytes): Data to append
Notes:
- Automatically switches to file storage if needed
- Transparent to caller after overflow
"""
def get(self, numbytes=-1, skip=False):
"""
Read data from buffer (delegates to underlying buffer).
Parameters:
- numbytes (int): Number of bytes to read
- skip (bool): Whether to consume data
Returns:
bytes: Read data
"""
def __len__(self):
"""Get number of bytes in buffer."""
def close(self):
"""Close buffer and clean up resources."""Important constants controlling buffer behavior and performance characteristics.
# Buffer copy operations
COPY_BYTES: int = 262144 # 256KB - chunk size for copying data between buffers
# String buffer limits
STRBUF_LIMIT: int = 8192 # 8KB - maximum size for simple string buffersCommon patterns for working with waitress buffer system.
from waitress.buffers import BytesIOBasedBuffer, TempfileBasedBuffer
# Memory-based buffer for small data
memory_buffer = BytesIOBasedBuffer()
memory_buffer.append(b"Hello, ")
memory_buffer.append(b"World!")
# Read data without consuming
data = memory_buffer.get()
print(data) # b"Hello, World!"
print(len(memory_buffer)) # 13
# Read and consume data
consumed = memory_buffer.get(5, skip=True)
print(consumed) # b"Hello"
print(len(memory_buffer)) # 8
# File-based buffer for large data
file_buffer = TempfileBasedBuffer()
large_data = b"x" * 1000000 # 1MB of data
file_buffer.append(large_data)
print(len(file_buffer)) # 1000000from waitress.buffers import OverflowableBuffer
# Buffer that overflows at 64KB
buffer = OverflowableBuffer(65536)
# Add small amounts of data (stays in memory)
for i in range(1000):
buffer.append(f"Line {i}\n".encode())
# Add large amount that triggers overflow to file
large_chunk = b"x" * 100000
buffer.append(large_chunk) # Now using temporary file
# Usage is transparent after overflow
data = buffer.get(1000)
print(len(data)) # 1000 bytesfrom waitress.buffers import ReadOnlyFileBasedBuffer
def serve_file_app(environ, start_response):
"""WSGI app that serves files efficiently."""
file_path = "/path/to/large/file.dat"
try:
with open(file_path, 'rb') as f:
# Create read-only buffer for efficient serving
file_buffer = ReadOnlyFileBasedBuffer(f, block_size=65536)
status = '200 OK'
headers = [
('Content-Type', 'application/octet-stream'),
('Content-Length', str(os.path.getsize(file_path)))
]
start_response(status, headers)
# Return buffer for efficient streaming
return file_buffer
except FileNotFoundError:
status = '404 Not Found'
headers = [('Content-Type', 'text/plain')]
start_response(status, headers)
return [b'File not found']
# The WSGI server will iterate over the buffer efficientlyfrom waitress.buffers import BytesIOBasedBuffer, TempfileBasedBuffer
# Create source buffer with data
source = BytesIOBasedBuffer()
source.append(b"Important data that needs to be copied")
# Create destination buffer from source
destination = TempfileBasedBuffer(from_buffer=source)
# Both buffers now contain the same data but are independent
source_data = source.get()
dest_data = destination.get()
assert source_data == dest_data
# Modifications to one don't affect the other
source.append(b" - modified")
assert source.get() != destination.get()
# Clean up
source.close()
destination.close()Guidelines for optimal buffer usage in different scenarios.
# Buffer selection guidelines:
SMALL_DATA = "Use BytesIOBasedBuffer (< 8KB)" # Fastest, in-memory
MEDIUM_DATA = "Use OverflowableBuffer (8KB-1MB)" # Adaptive
LARGE_DATA = "Use TempfileBasedBuffer (> 1MB)" # File-based
STATIC_FILES = "Use ReadOnlyFileBasedBuffer" # Optimized serving
# Memory usage patterns:
MEMORY_EFFICIENT = "Use file-based buffers for large data"
SPEED_OPTIMIZED = "Use memory buffers for frequently accessed data"
BALANCED = "Use OverflowableBuffer for variable-size data"
# Buffer lifecycle:
CLEANUP_IMPORTANT = "Always call close() on file-based buffers"
TEMPORARY_FILES = "Automatically cleaned up when closed"
MEMORY_BUFFERS = "Cleaned up by garbage collector"How buffers integrate with waitress HTTP processing pipeline.
# Request body buffering:
REQUEST_SMALL = "Stored in BytesIOBasedBuffer initially"
REQUEST_LARGE = "Overflows to TempfileBasedBuffer automatically"
REQUEST_STREAMING = "Processed incrementally as data arrives"
# Response buffering:
RESPONSE_HEADERS = "Buffered in memory for efficiency"
RESPONSE_BODY = "Uses appropriate buffer based on size"
RESPONSE_FILES = "Uses ReadOnlyFileBasedBuffer for static files"
# Connection management:
KEEP_ALIVE = "Buffers reused across requests on same connection"
PIPELINING = "Multiple request buffers managed per connection"
CLEANUP = "Buffers automatically cleaned up on connection close"Install with Tessl CLI
npx tessl i tessl/pypi-waitress