Skip to main content

A lightweight, framework-agnostic real-time library for Python applications.

Project description

PyStrands

A lightweight, framework-agnostic real-time communication library for Python applications.

Overview

PyStrands provides a client-server architecture for building real-time features. A Go-based WebSocket broker handles client connections, while Python backends process messages via TCP — enabling horizontal scaling and clean separation of concerns.

WebSocket Clients ←→ Go Broker ←TCP→ Python Backends

Features

  • Sync & Async clientsPyStrandsClient (threading) and AsyncPyStrandsClient (asyncio)
  • Room-based, private, and broadcast messaging
  • Connection request handling with auth & routing
  • Auto-reconnect with exponential backoff
  • Message queuing in the Go broker when backends are down
  • Framework-agnostic — works with Flask, FastAPI, Django, or standalone
  • 66 tests including real-world production scenarios

Installation

pip install pystrands

Quick Start

Running the Server

python -m pystrands server --ws-port 8080 --tcp-port 8081 --queue-size 1000

The server binary is automatically downloaded for your platform.

Options:

  • --ws-port — WebSocket port for clients (default: 8080)
  • --tcp-port — TCP port for Python backends (default: 8081)
  • --queue-size — Message buffer when no backends connected (default: 1000, 0 = disabled)

Async Client (Recommended)

import asyncio
from pystrands import AsyncPyStrandsClient

class ChatBackend(AsyncPyStrandsClient):
    async def on_connection_request(self, request):
        # Auth: check headers, assign room, set metadata
        auth = request.headers.get("Authorization", [None])[0]
        if auth != "Bearer valid-token":
            return False  # reject

        request.context.room_id = request.url.strip("/")
        request.context.metadata = {"role": "user"}
        return True  # accept

    async def on_message(self, message, context):
        print(f"[{context.room_id}] {context.client_id}: {message}")
        # Echo back to the room
        await self.send_room_message(context.room_id, f"echo: {message}")

    async def on_disconnect(self, context):
        print(f"{context.client_id} left {context.room_id}")

client = ChatBackend(host="localhost", port=8081)
asyncio.run(client.run_forever())

Sync Client

from pystrands import PyStrandsClient

class ChatBackend(PyStrandsClient):
    def on_connection_request(self, request):
        request.context.room_id = request.url.strip("/")
        return True

    def on_message(self, message, context):
        print(f"[{context.room_id}] {context.client_id}: {message}")
        self.send_room_message(context.room_id, f"echo: {message}")

client = ChatBackend(host="localhost", port=8081)
client.run_forever()

API Reference

PyStrandsClient / AsyncPyStrandsClient

# Constructor
client = PyStrandsClient(
    host="localhost",
    port=8081,
    auto_reconnect=True,       # reconnect on connection loss
    reconnect_delay=1.0,       # initial delay (seconds)
    max_reconnect_delay=30.0,  # max delay cap
    reconnect_backoff=2.0,     # exponential multiplier
)

Methods

Method Description
connect() Connect to the Go broker
disconnect() Cleanly disconnect
run_forever() Block until disconnect
broadcast_message(message) Send to all WebSocket clients
send_room_message(room_id, message) Send to a specific room
send_private_message(client_id, message) Send to a specific client

Callbacks (override these)

Callback When
on_connection_request(context) WebSocket client wants to connect. Return True/False.
on_new_connection(context) Client successfully connected
on_message(message, context) Client sent a message
on_disconnect(context) Client disconnected
on_error(error, context) Error occurred

Context Classes

Context

  • client_id — Unique client identifier
  • room_id — Room the client belongs to
  • metadata — Dict for custom data (auth info, user details, etc.)

ConnectionRequestContext

  • headers — HTTP headers from the WebSocket upgrade request
  • url — URL endpoint the client connected to
  • remote_addr — Client's IP address
  • context — The Context object (modify this for routing)
  • accepted — Whether to accept (default: True)

Architecture

┌──────────┐     WebSocket     ┌──────────┐      TCP       ┌──────────────┐
│  Browser  │ ←──────────────→ │ Go Broker │ ←────────────→ │ Python Backend│
│  Client   │                  │           │                │ (your code)  │
└──────────┘                   │  Queue:   │                └──────────────┘
                               │  [msg1]   │                ┌──────────────┐
                               │  [msg2]   │ ←────────────→ │ Python Backend│
                               │  [msg3]   │                │ (replica 2)  │
                               └──────────┘                └──────────────┘
  • Go Broker handles WebSocket connections, routing, and message queuing
  • Python Backends connect via TCP, process messages, send responses
  • Horizontal scaling — add more Python backends, broker load-balances
  • Message queue — if all backends go down, messages buffer in the broker and flush on reconnect

Requirements

  • Python >= 3.8

License

MIT — see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pystrands-1.0.0.tar.gz (19.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pystrands-1.0.0-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

File details

Details for the file pystrands-1.0.0.tar.gz.

File metadata

  • Download URL: pystrands-1.0.0.tar.gz
  • Upload date:
  • Size: 19.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pystrands-1.0.0.tar.gz
Algorithm Hash digest
SHA256 2488d3bd205c2e7b40e502800918a41a5808dd3b51e9a68c126d315a2f2239a1
MD5 2d1b014dc952138c10577e91d0938256
BLAKE2b-256 c29c5473e79e06e7b3f9aa1bbaa4765b06bbf24ac707ad4a91f8e3f057b3e70d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pystrands-1.0.0.tar.gz:

Publisher: publish.yml on pystrands/pystrands

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pystrands-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: pystrands-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 11.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pystrands-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 208d7f1dd5e0f1c1c188cdd084cf121c3a820942fedd68a1bee3db9eaaaf4e98
MD5 2ad145f0914adb6944021d0e31084ef1
BLAKE2b-256 710f8bc4919339e2ceb1f447c9d78a99701f37ec2a20a4b35560c698dbc5206f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pystrands-1.0.0-py3-none-any.whl:

Publisher: publish.yml on pystrands/pystrands

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page