Streaming I/O¶
cloudpathlib provides streaming I/O capabilities for cloud storage through Python's standard I/O interfaces.
Overview¶
By default, CloudPathLib downloads files to a local cache before opening them. While this works well for many use cases, it can be inefficient for:
- Large files that don't fit in memory or disk
- Partial reads where you only need to access part of a file
- Sequential processing where you read a file once and discard it
- Write-only workflows where you're generating data to upload
The streaming I/O system solves these problems by:
- Reading data directly from cloud storage using range requests
- Writing data directly to cloud storage using multipart/block uploads
- Providing standard Python file-like objects that work with any library
- Eliminating the need for local disk caching
Quick Start¶
Enable Streaming Mode¶
To use streaming I/O, set your client's file_cache_mode to FileCacheMode.streaming:
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
# Option 1: Set streaming mode on the client
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/file.txt", client=client)
with path.open("rt") as f:
for line in f:
print(line.strip())
# Option 2: Change mode on existing client
client = S3Client()
client.file_cache_mode = FileCacheMode.streaming
path = S3Path("s3://bucket/file.txt", client=client)
with path.open("rt") as f:
content = f.read()
# Option 3: Temporarily enable streaming
client = S3Client()
path = S3Path("s3://bucket/file.txt", client=client)
original_mode = path.client.file_cache_mode
path.client.file_cache_mode = FileCacheMode.streaming
with path.open("rt") as f:
content = f.read()
path.client.file_cache_mode = original_mode # Restore
Basic Examples¶
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
# Create a client with streaming enabled
client = S3Client(file_cache_mode=FileCacheMode.streaming)
# Read a text file
path = S3Path("s3://bucket/file.txt", client=client)
with path.open("rt") as f:
for line in f:
print(line.strip())
# Write a binary file
path = S3Path("s3://bucket/output.bin", client=client)
with path.open("wb") as f:
f.write(b"Hello, cloud!")
# Read binary data in chunks
path = S3Path("s3://bucket/large-file.bin", client=client)
with path.open("rb") as f:
while chunk := f.read(8192):
process(chunk)
API Reference¶
Prefer CloudPath.open()
The usual entry point is CloudPath.open() with FileCacheMode.streaming. CloudBufferedIO and CloudTextIO are also public for integrations that need to construct a provider-backed file object directly.
FileCacheMode Enum¶
Controls how CloudPath.open() handles file caching:
FileCacheMode.cloudpath_object: Default - cache files in CloudPath objectFileCacheMode.tmp_dir: Cache files in temporary directoryFileCacheMode.persistent: Cache files persistentlyFileCacheMode.close_file: Close file after readingFileCacheMode.streaming: Stream directly without caching
from cloudpathlib.enums import FileCacheMode
# Set on client initialization
client = S3Client(file_cache_mode=FileCacheMode.streaming)
# Or change dynamically
client.file_cache_mode = FileCacheMode.streaming
CloudPath.open()¶
Opens a cloud file in streaming mode when file_cache_mode is set to FileCacheMode.streaming.
CloudPath.open(
mode: str = "r",
buffering: int = -1,
encoding: Optional[str] = None,
errors: Optional[str] = None,
newline: Optional[str] = None,
*,
buffer_size: Optional[int] = None,
) -> Union[CloudBufferedIO, CloudTextIO, IO]
Parameters:
mode: File mode - binary ('rb','wb', etc.) or text ('r','w','rt','wt', etc.)buffering: Standard Python buffering control. Binary mode supports0for an unbuffered raw stream.encoding: Text encoding (default: platform locale, text mode only)errors: Error handling strategy (default:"strict", text mode only)newline: Newline handling (text mode only)buffer_size: Size of read/write buffer in bytes (default: 64 KiB)
Returns:
CloudBufferedIOfor binary modes (when streaming)CloudTextIOfor text modes (when streaming)- Standard file object (when not streaming)
CloudBufferedIO¶
Binary file-like object implementing io.BufferedIOBase.
Usually returned by CloudPath.open()
Most applications should let CloudPath.open() construct this class. Direct construction is supported when implementing file-object integrations.
Key Methods:
read(size=-1): Read up to size bytes (all if size is -1)read1(size=-1): Read up to size bytes with one underlying read callreadinto(b): Read bytes into a pre-allocated bufferwrite(b): Write bytesflush(): Flush write buffer to cloud storageseek(offset, whence=SEEK_SET): Change stream positiontell(): Return current stream positionclose(): Close file and finalize upload
Properties:
name: The cloud pathmode: File mode (e.g.,"rb","wb")closed: Whether the file is closed
Capability Flags:
readable(): Returns True for read modeswritable(): Returns True for write modesseekable(): ReturnsTruefor readable streams. Streaming writes are sequential and returnFalse.
CloudTextIO¶
Text file-like object implementing io.TextIOBase.
Usually returned by CloudPath.open()
Most applications should let CloudPath.open() construct this class. Direct construction is supported when implementing file-object integrations.
Key Methods:
read(size=-1): Read up to size charactersreadline(size=-1): Read one linereadlines(hint=-1): Read list of lineswrite(s): Write stringwritelines(lines): Write list of stringsflush(): Flush write bufferseek(offset, whence=SEEK_SET): Change positiontell(): Return current positionclose(): Close file
Properties:
name: The cloud pathmode: File mode (e.g.,"rt","wt")encoding: Text encodingerrors: Error handling strategynewlines: Newline(s) encounteredbuffer: Underlying binary buffer (CloudBufferedIO)closed: Whether the file is closed
Iteration:
CloudTextIO supports iteration:
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/file.txt", client=client)
with path.open("rt") as f:
for line in f:
process(line)
Usage Examples¶
Reading Large Files in Chunks¶
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/huge-file.csv", client=client)
# Process a large file without loading it entirely into memory
with path.open("rt") as f:
header = f.readline()
for line in f:
process_csv_line(line)
Partial File Reads¶
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/data.bin", client=client)
# Read just the header of a file
with path.open("rb") as f:
header = f.read(1024) # Read first 1KB
parse_header(header)
# Seek to specific position
f.seek(10000)
chunk = f.read(100)
Streaming Uploads¶
from cloudpathlib import AzureBlobPath, AzureBlobClient
from cloudpathlib.enums import FileCacheMode
import json
client = AzureBlobClient(file_cache_mode=FileCacheMode.streaming)
path = AzureBlobPath("az://container/output.json", client=client)
# Write data directly to cloud without local file
with path.open("wt") as f:
f.write('{"items": [\n')
for i, item in enumerate(generate_items()):
if i > 0:
f.write(',\n')
f.write(json.dumps(item))
f.write('\n]}')
Using with pandas¶
import pandas as pd
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
# Read CSV directly from cloud
read_path = S3Path("s3://bucket/data.csv", client=client)
with read_path.open("rt") as f:
df = pd.read_csv(f)
# Write CSV directly to cloud
write_path = S3Path("s3://bucket/output.csv", client=client)
with write_path.open("wt") as f:
df.to_csv(f, index=False)
Using with PIL/Pillow¶
from PIL import Image
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
# Read image
read_path = S3Path("s3://bucket/image.jpg", client=client)
with read_path.open("rb") as f:
img = Image.open(f)
img.show()
# Write image
write_path = S3Path("s3://bucket/output.png", client=client)
with write_path.open("wb") as f:
img.save(f, format="PNG")
Custom Buffer Size¶
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
# Use larger buffer for better throughput on fast connections
path = S3Path("s3://bucket/large-file.bin", client=client)
with path.open("rb", buffer_size=1024*1024) as f:
data = f.read()
# Use smaller buffer for memory-constrained environments
path = S3Path("s3://bucket/file.txt", client=client)
with path.open("rt", buffer_size=8192) as f:
for line in f:
process(line)
Performance Considerations¶
Buffer Size¶
The buffer_size parameter controls how much data is fetched from/written to cloud storage in each request:
- Larger buffers (256 KiB - 1 MiB): Better throughput, fewer requests, more memory
- Smaller buffers (8 KiB - 64 KiB): Lower memory usage, more requests, lower throughput
- Default (64 KiB): Good balance for most use cases
Read Patterns¶
- Sequential reads: Optimal performance - data is fetched ahead as needed
- Random seeks: Each seek may trigger a new range request - less efficient
- Small random reads: Consider downloading the file to cache instead
Write Contract¶
Object storage is fundamentally a sequential, write-once medium. Streaming mode reflects that contract:
| Mode | Streaming behaviour |
|---|---|
wb, w, xb, x |
True streaming — data is forwarded to the provider as it arrives |
ab, a, r+b, r+, w+b, w+ |
Falls back to cache — the object is downloaded, mutated locally, then re-uploaded on close. Semantics are correct; performance matches the cached path. |
Attempting to seek backward on a write-only streaming stream raises
io.UnsupportedOperation because the provider has already accepted
the earlier bytes.
Multipart/Block Uploads¶
For write operations, the streaming I/O system automatically handles:
- S3: Multipart upload; non-final parts are buffered until they reach the provider minimum of 5 MiB (S3 rejects smaller non-final parts). The final part may be smaller than 5 MiB.
- Azure: Block blob staging — blocks grow adaptively during very large uploads and are committed on close.
- GCS: Resumable upload (
blob.open("wb")) — data streams incrementally to GCS without in-memory buffering.
Provider-Specific Behavior¶
AWS S3¶
- Uses boto3
get_object()withRangeheader for reads - Uses boto3 multipart upload API for writes
- Supports all S3-compatible storage (MinIO, Ceph, etc.)
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/file.txt", client=client)
with path.open("rt") as f:
content = f.read()
Azure Blob Storage¶
- Uses Azure SDK
download_blob()with offset/length for reads - Uses block blob staging and commit for writes
- Compatible with Azure Data Lake Storage Gen2
from cloudpathlib import AzureBlobPath, AzureBlobClient
from cloudpathlib.enums import FileCacheMode
client = AzureBlobClient(file_cache_mode=FileCacheMode.streaming)
path = AzureBlobPath("az://container/file.txt", client=client)
with path.open("rt") as f:
content = f.read()
Google Cloud Storage¶
- Uses GCS SDK
download_as_bytes()with start/end for reads - Uses a resumable
blob.open("wb")stream for writes - Supports GCS-specific features through client configuration
from cloudpathlib import GSPath, GSClient
from cloudpathlib.enums import FileCacheMode
client = GSClient(file_cache_mode=FileCacheMode.streaming)
path = GSPath("gs://bucket/file.txt", client=client)
with path.open("rt") as f:
content = f.read()
HTTP and HTTPS¶
- Requires servers to honor byte-range requests for streaming reads
- Uses the client's configured
write_file_http_methodfor writes - Spools single-request upload bodies with bounded memory, using a temporary file above 8 MiB
Comparison with Cached Mode¶
| Feature | Streaming (FileCacheMode.streaming) |
Cached (default) |
|---|---|---|
| Disk usage | None for cloud providers; HTTP uploads may use a temporary spool | Full file size |
| Memory usage | Configurable buffer | Varies |
| Read performance | Sequential: Good Random: Moderate |
Fast (local disk) |
| Write performance | Good (direct upload) | Fast write, slower close |
| Partial reads | Efficient | Downloads full file |
| Large files | Excellent | Limited by disk space |
| Offline access | No | Yes (after download) |
| Compatibility | Standard I/O interfaces | Standard I/O interfaces |
Best Practices¶
When to Use Streaming I/O¶
✅ Good use cases:
- Large files that don't fit in memory/disk
- Reading only part of a file (e.g., headers, metadata)
- Sequential processing (one-pass reads)
- Direct upload of generated content
- Integration with libraries that accept file-like objects
❌ Consider caching instead:
- Small files (< 10 MB)
- Frequent random access to same file
- Multiple passes over the same data
- Offline processing
- Maximum read performance required
- Libraries that require file paths (
.fspathnot available in streaming mode)
Error Handling¶
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/file.txt", client=client)
try:
with path.open("rt") as f:
content = f.read()
except FileNotFoundError:
print("File not found in cloud storage")
except PermissionError:
print("Access denied")
except Exception as e:
print(f"Error: {e}")
Resource Management¶
Always use context managers to ensure proper cleanup:
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/file.txt", client=client)
# ✅ Good - file is automatically closed
with path.open("rt") as f:
content = f.read()
# ❌ Bad - must remember to close manually
f = path.open("rt")
content = f.read()
f.close() # Easy to forget!
Streaming Mode Limitations¶
When using FileCacheMode.streaming, certain CloudPath features are not available because streaming mode doesn't create cached files on disk:
Not Available:
- .fspath property - Raises CloudPathNotImplementedError
- .__fspath__() method - Raises CloudPathNotImplementedError
- Passing CloudPath as os.PathLike to libraries that need file paths
Workaround:
Use CloudPath.open() and pass the file-like object to libraries that accept file handles instead of file paths.
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
import pandas as pd
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/data.csv", client=client)
# ❌ This will raise an error in streaming mode
# df = pd.read_csv(path.fspath)
# ✅ Use this instead - pass the open file handle
with path.open("rt") as f:
df = pd.read_csv(f)
Compatibility¶
Python I/O Interfaces¶
The streaming I/O classes are fully compatible with Python's I/O hierarchy:
import io
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/file.bin", client=client)
with path.open("rb") as f:
assert isinstance(f, io.IOBase)
assert isinstance(f, io.BufferedIOBase)
path = S3Path("s3://bucket/file.txt", client=client)
with path.open("rt") as f:
assert isinstance(f, io.IOBase)
assert isinstance(f, io.TextIOBase)
Third-Party Libraries¶
Works with any library that accepts file-like objects:
- Data processing: pandas, NumPy, PyArrow
- Images: PIL/Pillow, OpenCV
- Compression: gzip, zipfile, tarfile
- Serialization: pickle, json, yaml
- Scientific: h5py, netCDF4
Troubleshooting¶
"File not found" errors¶
Ensure the file exists and you have read permissions:
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/file.txt", client=client)
if path.exists():
with path.open("rt") as f:
content = f.read()
Slow performance¶
Try increasing buffer size:
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/file.txt", client=client)
# Larger buffer for faster networks
with path.open("rb", buffer_size=1024*1024) as f:
data = f.read()
Out of memory¶
Try smaller buffer size or process in chunks:
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/huge.bin", client=client)
# Process large file in chunks
with path.open("rb", buffer_size=8192) as f:
while chunk := f.read(8192):
process_chunk(chunk)
Migration Guide¶
From Cached to Streaming¶
Before:
from cloudpathlib import S3Path
path = S3Path("s3://bucket/file.txt")
with path.open("rt") as f: # Downloads to cache
content = f.read()
After:
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
# Option 1: Set on client initialization
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/file.txt", client=client)
with path.open("rt") as f: # Streams directly
content = f.read()
# Option 2: Change client mode
client = S3Client()
path = S3Path("s3://bucket/file.txt", client=client)
path.client.file_cache_mode = FileCacheMode.streaming
with path.open("rt") as f: # Streams directly
content = f.read()
Advanced Topics¶
Custom Clients¶
Pass custom clients with specific configurations:
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
# Custom S3-compatible endpoint and upload metadata
client = S3Client(
file_cache_mode=FileCacheMode.streaming,
endpoint_url="https://objects.example.com",
addressing_style="path",
extra_args={"ServerSideEncryption": "AES256"},
)
path = S3Path("s3://bucket/file.txt", client=client)
with path.open("rt") as f:
content = f.read()
Multiple Files¶
Process multiple files efficiently:
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
bucket = S3Path("s3://bucket/", client=client)
for file_path in bucket.glob("*.csv"):
with file_path.open("rt") as f:
process_csv(f)
Encoding Detection¶
For files with unknown encoding:
import chardet
from cloudpathlib import S3Path, S3Client
from cloudpathlib.enums import FileCacheMode
client = S3Client(file_cache_mode=FileCacheMode.streaming)
path = S3Path("s3://bucket/file.txt", client=client)
# Read a small sample to detect encoding
with path.open("rb") as f:
sample = f.read(10000)
detected = chardet.detect(sample)
encoding = detected['encoding']
# Re-open with detected encoding
with path.open("rt", encoding=encoding) as f:
content = f.read()
Context Manager for Temporary Streaming¶
Use a context manager to temporarily enable streaming mode:
from contextlib import contextmanager
from cloudpathlib import S3Client
from cloudpathlib.enums import FileCacheMode
@contextmanager
def streaming_mode(client):
"""Temporarily enable streaming mode on a client."""
original_mode = client.file_cache_mode
try:
client.file_cache_mode = FileCacheMode.streaming
yield client
finally:
client.file_cache_mode = original_mode
# Usage
client = S3Client()
path = S3Path("s3://bucket/file.txt", client=client)
with streaming_mode(client):
with path.open("rt") as f:
content = f.read() # Uses streaming
# Back to cached mode
with path.open("rt") as f:
content = f.read() # Uses caching