Typed wrapper for python-socketio with Pydantic validation and dependency injection.
Project description
zndraw-socketio
Typed wrapper for python-socketio with Pydantic validation and dependency injection.
Installation
pip install zndraw-socketio
Optional extras for client transports:
pip install zndraw-socketio[client]
pip install zndraw-socketio[asyncio-client]
Usage
Wrap any socketio instance
import socketio
from pydantic import BaseModel
from zndraw_socketio import wrap
class Ping(BaseModel):
message: str
class Pong(BaseModel):
reply: str
# Wrap any existing socketio instance
tsio = wrap(socketio.AsyncClient())
# Emit with automatic event name derivation (Ping -> "ping")
await tsio.emit(Ping(message="Hello, World!"))
# Emit with explicit event name
await tsio.emit("my-ping", Ping(message="Hello, World!"))
# Call with typed response
response = await tsio.call(Ping(message="Hello"), response_model=Pong)
# response is typed as Pong
Handler registration with validation
# Event name derived from model class (Ping -> "ping")
@tsio.on(Ping)
async def handle_ping(data: Ping) -> Pong:
return Pong(reply=data.message)
# Or use function name as event name
@tsio.event
async def ping(data: Ping) -> Pong:
return Pong(reply=data.message)
Union response types
from typing import Annotated, Literal
from pydantic import BaseModel, Discriminator
class Success(BaseModel):
kind: Literal["success"] = "success"
data: str
class Error(BaseModel):
kind: Literal["error"] = "error"
message: str
# Simple union
response = await tsio.call(request, response_model=Success | Error)
# Discriminated union
ResponseType = Annotated[Success | Error, Discriminator("kind")]
response = await tsio.call(request, response_model=ResponseType)
Custom event names
from typing import ClassVar
class CustomEvent(BaseModel):
event_name: ClassVar[str] = "my_custom_event"
data: str
# Uses "my_custom_event" instead of "custom_event"
await tsio.emit(CustomEvent(data="hello"))
Exception handlers
from zndraw_socketio import wrap, EventContext
tsio = wrap(socketio.AsyncServer(async_mode="asgi"))
@tsio.exception_handler(ValueError)
async def handle_value_error(ctx: EventContext, exc: ValueError):
return {"error": "value_error", "message": str(exc)}
# Namespace-specific
@tsio.exception_handler(ValueError, namespace="/chat")
async def handle_chat_error(ctx: EventContext, exc: ValueError):
return {"error": "chat_error", "message": str(exc)}
Dependency injection
from typing import Annotated
from fastapi import Depends
from zndraw_socketio import wrap
async def get_redis():
return my_redis_pool
RedisDep = Annotated[AsyncRedis, Depends(get_redis)]
tsio = wrap(socketio.AsyncServer(async_mode="asgi"))
@tsio.on(RoomLeave)
async def room_leave(sid: str, data: RoomLeave, redis: RedisDep) -> RoomLeaveResponse:
await redis.delete(f"presence:{data.room_id}:{sid}")
return RoomLeaveResponse(status="ok")
When FastAPI is not installed, import Depends from zndraw_socketio:
from zndraw_socketio import Depends
FastAPI integration
import socketio
from typing import Annotated
from fastapi import FastAPI, Depends
from zndraw_socketio import wrap, AsyncServerWrapper
app = FastAPI()
tsio = wrap(socketio.AsyncServer(async_mode="asgi"))
SioServer = Annotated[AsyncServerWrapper, Depends(tsio)]
@app.post("/notify")
async def notify(server: SioServer):
await server.emit("notification", {"msg": "hello"})
return {"status": "sent"}
combined_app = socketio.ASGIApp(tsio, app)
SimpleClient support
tsio = wrap(socketio.SimpleClient())
tsio.connect("http://localhost:5000")
tsio.emit(Ping(message="Hello"))
response = tsio.call(Ping(message="Hello"), response_model=Pong)
event_name, data = tsio.receive(response_model=Pong)
tsio.disconnect()
Supported socketio types
wrap() auto-detects the socketio instance type:
| socketio type | Wrapper class |
|---|---|
AsyncClient |
AsyncClientWrapper |
AsyncServer |
AsyncServerWrapper |
Client |
SyncClientWrapper |
Server |
SyncServerWrapper |
SimpleClient |
SimpleClientWrapper |
AsyncSimpleClient |
AsyncSimpleClientWrapper |
License
MIT License
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zndraw_socketio-0.1.0.tar.gz.
File metadata
- Download URL: zndraw_socketio-0.1.0.tar.gz
- Upload date:
- Size: 119.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50395564929653897bdfb987c8814600932956b907a611474dab51eba1b4843f
|
|
| MD5 |
2bac0203089dfe7b76ab972a04dcbfd1
|
|
| BLAKE2b-256 |
4e04009dde6313f73eec7a18a19fc72b2fdab227e163754ec31d76b616e201e4
|
File details
Details for the file zndraw_socketio-0.1.0-py3-none-any.whl.
File metadata
- Download URL: zndraw_socketio-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b23bc0c0d54218a8ec250dacc717efc0be6f5f5f22f6165e1e1bc7f68b8657fe
|
|
| MD5 |
636287e5b1d122efb31d85d220d74a03
|
|
| BLAKE2b-256 |
4b46827361b489e4432f7d5d37a700b5ab0f85a729caf83848b553b3337e859b
|