Independent BSON codec for Python that doesn't depend on MongoDB
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Socket patching capabilities that extend Python socket objects with atomic BSON transmission methods. This enables seamless BSON communication over network connections with automatic serialization and deserialization.
Patches the Python socket class to add BSON object transmission methods, enabling atomic send and receive operations for BSON data.
def patch_socket():
"""
Patch the Python socket class with BSON transmission methods.
Adds the following methods to socket objects:
- recvbytes(bytes_needed, sock_buf=None): Read exact number of bytes
- recvobj(): Read complete BSON object
- sendobj(obj): Send BSON object atomically
Note: Must be called before creating socket objects to enable BSON methods
"""Usage example:
import socket
import bson
# Patch socket class first
bson.patch_socket()
# Create socket with BSON capabilities
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Now socket has BSON methods available
# sock.sendobj({"message": "hello"})
# obj = sock.recvobj()Receive an exact number of bytes from a socket atomically, handling partial reads and connection closures gracefully.
def recvbytes(self, bytes_needed, sock_buf=None):
"""
Atomically read exact number of bytes from socket.
This method is added to socket objects by patch_socket().
Parameters:
- bytes_needed: int, exact number of bytes to read
- sock_buf: BytesIO buffer to append to (optional, creates new if None)
Returns:
BytesIO: Buffer containing exactly bytes_needed bytes, or None if socket closed
Raises:
socket.error: On network errors
"""Usage example:
import socket
import bson
from io import BytesIO
bson.patch_socket()
# Server side example
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.bind(('localhost', 8080))
server_sock.listen(1)
conn, addr = server_sock.accept()
# Read exactly 100 bytes
data_buffer = conn.recvbytes(100)
if data_buffer is not None:
raw_data = data_buffer.getvalue()
print(f"Received {len(raw_data)} bytes")
else:
print("Connection closed by client")
# Reuse buffer for multiple reads
buffer = BytesIO()
header = conn.recvbytes(4, buffer) # Read 4-byte header
if header is not None:
payload = conn.recvbytes(96, header) # Read 96 more bytes into same buffer
if payload is not None:
total_data = payload.getvalue() # Now contains 100 bytes totalReceive complete BSON objects from socket, automatically handling message framing and deserialization.
def recvobj(self):
"""
Atomically receive a complete BSON object from socket.
This method is added to socket objects by patch_socket().
Handles BSON message framing automatically by reading length header first.
Returns:
dict: Deserialized BSON object, or None if socket closed
Raises:
socket.error: On network errors
ValueError: If BSON data is malformed
"""Usage example:
import socket
import bson
bson.patch_socket()
# Server receiving BSON objects
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.bind(('localhost', 8080))
server_sock.listen(1)
conn, addr = server_sock.accept()
while True:
# Receive BSON object
obj = conn.recvobj()
if obj is None:
print("Client disconnected")
break
print(f"Received: {obj}")
# Process the received object
if obj.get('command') == 'quit':
break
conn.close()Send Python objects as BSON over socket connections with automatic serialization and framing.
def sendobj(self, obj):
"""
Atomically send a BSON object over socket.
This method is added to socket objects by patch_socket().
Automatically serializes object to BSON and sends with proper framing.
Parameters:
- obj: dict or BSONCoding object to send
Raises:
socket.error: On network errors
UnknownSerializerError: If object cannot be serialized
"""Usage example:
import socket
import bson
bson.patch_socket()
# Client sending BSON objects
client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_sock.connect(('localhost', 8080))
# Send various BSON objects
messages = [
{"type": "greeting", "message": "Hello server!"},
{"type": "data", "values": [1, 2, 3, 4, 5]},
{"type": "user", "name": "Alice", "age": 30},
{"command": "quit"}
]
for msg in messages:
client_sock.sendobj(msg)
print(f"Sent: {msg}")
client_sock.close()# Server (server.py)
import socket
import threading
import bson
bson.patch_socket()
def handle_client(conn, addr):
print(f"Connected by {addr}")
while True:
try:
# Receive BSON object
obj = conn.recvobj()
if obj is None:
print(f"Client {addr} disconnected")
break
print(f"Received from {addr}: {obj}")
# Echo back with server timestamp
import time
response = {
"echo": obj,
"server_time": time.time(),
"status": "received"
}
conn.sendobj(response)
except Exception as e:
print(f"Error handling {addr}: {e}")
break
conn.close()
# Start server
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('localhost', 8080))
server.listen(5)
print("Server listening on port 8080")
while True:
conn, addr = server.accept()
thread = threading.Thread(target=handle_client, args=(conn, addr))
thread.start()# Client (client.py)
import socket
import bson
import time
bson.patch_socket()
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 8080))
# Send test messages
test_data = [
{"message": "Hello", "id": 1},
{"data": [10, 20, 30], "type": "array"},
{"user": {"name": "Bob", "role": "admin"}, "nested": True}
]
for data in test_data:
# Send object
client.sendobj(data)
# Receive response
response = client.recvobj()
if response:
print(f"Server response: {response}")
time.sleep(1)
client.close()All socket methods can raise standard socket exceptions:
socket.error: General network errorsConnectionResetError: Connection closed by peerConnectionAbortedError: Connection abortedTimeoutError: Socket timeoutBSON-specific errors during network operations:
UnknownSerializerError: Cannot serialize object in sendobj()ValueError: Malformed BSON data in recvobj()Methods return None when the remote end closes the connection cleanly:
obj = sock.recvobj()
if obj is None:
# Connection closed cleanly
print("Peer disconnected")
else:
# Process received object
handle_message(obj)recvbytes() reads data in chunks up to 32KB to handle large messages efficientlyrecvobj() first reads the 4-byte BSON length header, then the remaining messagesendobj() uses socket.sendall() to ensure complete transmissionInstall with Tessl CLI
npx tessl i tessl/pypi-bson