Streaming
Crous supports stream-based I/O for writing to and reading from any file-like object, including files, sockets, io.BytesIO, and network streams.
Stream Writing with dump_to_stream
Write serialized binary data to any writable stream (any object with a write() method).
stream_write.py
import crous
import io
data = {"name": "Alice", "scores": [98, 95, 100]}
# Write to BytesIO
buffer = io.BytesIO()
crous.dump_to_stream(data, buffer)
# Check the result
buffer.seek(0)
print(f"Written {len(buffer.getvalue())} bytes")Stream Reading with load_from_stream
Read and deserialize from any readable stream (any object with a read() method).
stream_read.py
import crous
import io
# Write data
data = {"key": "value"}
buffer = io.BytesIO()
crous.dump_to_stream(data, buffer)
# Read it back
buffer.seek(0)
result = crous.load_from_stream(buffer)
assert result == dataFile Streams
The dump and load functions accept both file paths and file objects:
file_streams.py
import crous
data = {"users": [{"name": "Alice"}, {"name": "Bob"}]}
# String path — Crous handles opening/closing
crous.dump(data, "output.crous")
result = crous.load("output.crous")
# File object — you manage the lifecycle
with open("output.crous", "wb") as f:
crous.dump(data, f)
with open("output.crous", "rb") as f:
result = crous.load(f)Multi-Record Streaming
You can write multiple records to a single stream. Each record includes its own header, so they can be read back sequentially.
multi_record.py
import crous
import io
buffer = io.BytesIO()
# Write multiple records
records = [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"},
{"id": 3, "name": "Charlie"},
]
for record in records:
crous.dump_to_stream(record, buffer)
# Read them back
buffer.seek(0)
for _ in range(len(records)):
record = crous.load_from_stream(buffer)
print(record)Stream Position
When reading multiple records, the stream position advances after each
load_from_stream call. Make sure to seek to position 0 if you want to re-read from the beginning.Network Sockets
Since Crous works with any file-like object, you can use it with sockets wrapped in socket.makefile():
socket_example.py
import crous
import socket
# Server side
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 9999))
server.listen(1)
conn, addr = server.accept()
# Wrap socket as file object
stream = conn.makefile('rwb')
# Read a message
data = crous.load_from_stream(stream)
print(f"Received: {data}")
# Send a response
crous.dump_to_stream({"status": "ok"}, stream)
stream.flush() # important for sockets!Buffering
When using sockets, remember to call
flush() after writing to ensure data is actually sent. Crous does not automatically flush the stream.BytesIO Convenience
For in-memory operations, io.BytesIO works seamlessly:
import crous
import io
# dumps/loads is simpler for pure in-memory use
binary = crous.dumps(data) # returns bytes directly
result = crous.loads(binary) # accepts bytes directly
# dump_to_stream/load_from_stream for stream-based use
buf = io.BytesIO()
crous.dump_to_stream(data, buf) # writes to stream
buf.seek(0)
result = crous.load_from_stream(buf) # reads from stream