Metadata-Version: 2.4
Name: larzws
Version: 0.1.0
Summary: WebSockets in pure Python — RFC 6455 server and client with framing, masking, ping/pong, and fragmentation. No async, zero dependencies.
Author: larz-scripter
License: MIT
Project-URL: Homepage, https://github.com/larz-scripter/larzws
Project-URL: Repository, https://github.com/larz-scripter/larzws
Project-URL: Documentation, https://github.com/larz-scripter/larzws#readme
Project-URL: Issues, https://github.com/larz-scripter/larzws/issues
Keywords: websocket,websockets,rfc6455,realtime,server,client,networking,websockets-alternative,zero-dependency,pure-python
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# larzws

**WebSockets in pure Python. Zero dependencies.**

A real RFC 6455 WebSocket **server and client** with nothing to install — no
`websockets`, no `aiohttp`, no async runtime. Handshake, framing, masking,
fragmentation, ping/pong, and close are handled; you just send and receive.

```python
from larzws import serve, connect

# server — echo
def handler(ws):
    for message in ws:                 # iterate messages until the client leaves
        ws.send("echo: " + message)
serve(handler, "127.0.0.1", 8765)

# client
ws = connect("ws://127.0.0.1:8765")
ws.send("hello")
print(ws.recv())                       # "echo: hello"
ws.close()
```

## What makes it different

- **No async, no dependency.** Most WebSocket libraries drag in an async stack.
  larzws is plain sockets + threads — drop it into any script or service.
- **Server *and* client, both real.** Full RFC 6455: the Upgrade handshake,
  binary framing with 7/16/64-bit lengths, client-side masking, fragmentation
  reassembly, automatic ping→pong, and clean close.
- **Text and binary.** `str` messages go as text frames, `bytes` as binary; you
  get back the right type.
- **Easy to embed.** Threaded server (one thread per connection) with
  `start_background()` for tests and apps.

Pairs naturally with [larzrpc](https://github.com/larz-scripter/larzrpc) for
realtime request/response over a socket.

## Install

```bash
pip install larzws
```

## Server

```python
from larzws import WebSocketServer, serve

def handler(ws):
    ws.send("welcome")
    for message in ws:
        ws.send("you said: " + message)

serve(handler, "0.0.0.0", 8765)          # blocking

# or embed it
server = WebSocketServer("127.0.0.1", 8765, handler)
server.start_background()                # daemon thread; server.port is resolved
...
server.shutdown()
```

## Client

```python
from larzws import connect

ws = connect("ws://127.0.0.1:8765/chat")
ws.send("hi")               # text
ws.send(b"\x00\x01")        # binary
msg = ws.recv()             # str or bytes, or None when closed
for msg in ws:              # or iterate until close
    ...
ws.ping()
ws.close()
```

## Scope

larzws speaks plain `ws://` (not `wss://`/TLS in this version — terminate TLS at a
reverse proxy if you need it) and uses a thread per connection, which is ideal for
tools, internal services, dashboards, and realtime features up to moderate
concurrency. For tens of thousands of idle connections you'd want an async
library; for everything else, this is refreshingly simple.

## Tests

```bash
python -m unittest discover -s tests -v   # 14 tests: RFC handshake, framing, e2e echo
```

## The Larz stack

Pure-Python, zero-dependency building blocks: **[larz](https://github.com/larz-scripter/larz)** · **[larzchain](https://github.com/larz-scripter/larzchain)** · **[larzmoney](https://github.com/larz-scripter/larzmoney)** · **[larzcrypt](https://github.com/larz-scripter/larzcrypt)** · **[larzdb](https://github.com/larz-scripter/larzdb)** · **[larzagent](https://github.com/larz-scripter/larzagent)** · **[larzchart](https://github.com/larz-scripter/larzchart)** · **[larzmark](https://github.com/larz-scripter/larzmark)** · **[larztask](https://github.com/larz-scripter/larztask)** · **[larzvault](https://github.com/larz-scripter/larzvault)** · **[larzvm](https://github.com/larz-scripter/larzvm)** · **[larzcache](https://github.com/larz-scripter/larzcache)** · **[larzvalidate](https://github.com/larz-scripter/larzvalidate)** · **[larzid](https://github.com/larz-scripter/larzid)** · **[larzrpc](https://github.com/larz-scripter/larzrpc)** · **[larzstate](https://github.com/larz-scripter/larzstate)** · **[larzhttp](https://github.com/larz-scripter/larzhttp)** · **[larzconf](https://github.com/larz-scripter/larzconf)** · **[larzcron](https://github.com/larz-scripter/larzcron)** · **[larzlimit](https://github.com/larz-scripter/larzlimit)** · **[larzlog](https://github.com/larz-scripter/larzlog)** · **[larzcli](https://github.com/larz-scripter/larzcli)** · **[larzretry](https://github.com/larz-scripter/larzretry)** · **[larztime](https://github.com/larz-scripter/larztime)** · **[larzpdf](https://github.com/larz-scripter/larzpdf)** · **[larzpack](https://github.com/larz-scripter/larzpack)** · **[larztemplate](https://github.com/larz-scripter/larztemplate)** · **[larzcolor](https://github.com/larz-scripter/larzcolor)** · **[larztable](https://github.com/larz-scripter/larztable)** · **[larzjson](https://github.com/larz-scripter/larzjson)** · **[larzbus](https://github.com/larz-scripter/larzbus)** · **[larzmigrate](https://github.com/larz-scripter/larzmigrate)** · **[larzgraph](https://github.com/larz-scripter/larzgraph)** · **larzws**

## License

MIT © larz-scripter
