Skip to main content

ZnDraw SocketIO

This package provides an opinionated typed interface to the python-socketio library using pydantic models.

from zndraw_socketio import wrap
from pydantic import BaseModel
import socketio

sio = wrap(socketio.AsyncClient())  # or AsyncServer, Client, Server, etc.

Emit Pattern

class Ping(BaseModel):
    message: str

# kwargs are passed to socketio's emit method
# emits {"message": "Hello, World!"} to "ping"
await sio.emit(Ping(message="Hello, World!"), **kwargs)
# emits {"message": "Hello, World!"} to "my-ping"
await sio.emit("my-ping", Ping(message="Hello, World!"), **kwargs)
# standard sio behaviour
await sio.emit("event", {"payload": ...})

Call / RPC Pattern

class Pong(BaseModel):
    reply: str

# emits {"message": "Hello, World!"} to "ping" and receives Pong(reply=...) in return
response = await sio.call(Ping(message="Hello, World!"), response_model=Pong)
assert isinstance(response, Pong)
# emits {"message": "Hello, World!"} to "my-ping" and receives Pong(reply=...) in return
response = await sio.call("my-ping", Ping(message="Hello, World!"), response_model=Pong)
assert isinstance(response, Pong)
# standard sio behaviour
response = await sio.call("event", {"payload": ...})
# standard response obj, typically dict

Handler Registration

Handlers are registered with @sio.on() or @sio.event and get automatic Pydantic validation from type hints.

tsio = wrap(socketio.AsyncServer(async_mode="asgi"))

@tsio.on(Ping)
async def handle_ping(sid: str, data: Ping) -> Pong:
    return Pong(reply=data.message)

# or use the function name as the event name
@tsio.event
async def ping(sid: str, data: Ping) -> Pong:
    return Pong(reply=data.message)

Outbound Event Documentation

Handlers that emit events to other channels can declare them with emits:

class SessionLeft(BaseModel):
    room_id: str
    user_id: str

@tsio.on("disconnect", emits=[SessionLeft])
async def handle_disconnect(sid: str) -> None:
    await tsio.emit(SessionLeft(room_id="room1", user_id="user1"), room="room1")

These appear as action: "send" operations in the generated AsyncAPI schema.

REST Endpoint Emits

REST endpoints can also declare socket event emissions using the Emits annotation. These are auto-discovered when tsio.app is set and appear as x-rest-triggers in the AsyncAPI schema.

from typing import Annotated
from fastapi import Depends, FastAPI
from zndraw_socketio import Emits, AsyncServerWrapper, wrap

app = FastAPI()
tsio = wrap(socketio.AsyncServer(async_mode="asgi"))
tsio.app = app

@app.put("/{key}/selection")
async def update_selection(
    sio: Annotated[AsyncServerWrapper, Depends(tsio), Emits(SessionLeft)],
    key: str,
) -> dict:
    await sio.emit(SessionLeft(room_id=key, user_id="..."), room=key)
    return {"status": "ok"}

AsyncAPI Schema Generation

Generate an AsyncAPI 3.0 specification from registered handlers:

schema = tsio.asyncapi_schema(title="My API", version="1.0.0")

Event Names

By default, the event name is the class name in snake_case. You can customize it by setting the event_name attribute.

class CustomEvent(BaseModel):
    ...

get_event_name(CustomEvent) == "custom_event"

You can override it like this:

from typing import ClassVar

class CustomEvent(BaseModel):
    event_name: ClassVar[str] = "my_custom_event"

Dependency Injection (Depends)

Handlers support FastAPI-style Depends() for dependency injection.

from typing import Annotated
from zndraw_socketio import wrap, Depends

async def get_redis() -> Redis:
    return Redis()

RedisDep = Annotated[Redis, Depends(get_redis)]

@tsio.on(Ping)
async def handle(sid: str, data: Ping, redis: RedisDep) -> Pong:
    await redis.set("last_ping", data.message)
    return Pong(reply=data.message)

FastAPI App Integration

To use dependencies that need request.app (e.g. accessing app.state), set the .app property on the wrapper. The app can be set after handler registration — it is resolved at event time, not at registration time.

from fastapi import FastAPI, Request

app = FastAPI()
tsio = wrap(socketio.AsyncServer(async_mode="asgi"))

def get_db(request: Request) -> Database:
    return request.app.state.db

@tsio.on(Ping)
async def handle(sid: str, data: Ping, db: Annotated[Database, Depends(get_db)]) -> Pong:
    ...

# Set app later — e.g. in a lifespan, after handler registration
tsio.app = app

Exception Handlers

Server wrappers support exception handlers similar to FastAPI.

from zndraw_socketio import EventContext

@tsio.exception_handler(ValueError)
async def handle_error(ctx: EventContext, exc: ValueError):
    return {"error": str(exc)}

Union Return Types

You might want to return Response | ErrorResponse from an event handler.

[!NOTE] If your responses share fields, it is recommended to add a discriminator field to avoid ambiguity.

class ProblemDetail(BaseModel):
    """RFC 9457 Problem Details."""
    kind: Literal["error"] = "error"
    type: str = "about:blank"
    title: str
    status: int
    detail: str | None = None
    instance: str | None = None

class Response(BaseModel):
    kind: Literal["response"] = "response"
    data: str

class ServerRequest(BaseModel):
    query: str

response = await sio.call(
    ServerRequest(query="..."),
    response_model=Response | ProblemDetail,
)

Metadata

Release files for zndraw-socketio 0.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zndraw-socketio 0.1.6
File Size Uploaded Provenance
zndraw_socketio-0.1.6.tar.gz 203.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for zndraw-socketio 0.1.6
File Interpreter ABI Platform
zndraw_socketio-0.1.6-py3-none-any.whl Python 3 none any Details

Total release size: 223.7 kB

Release files / zndraw_socketio-0.1.6.tar.gz

Download URL zndraw_socketio-0.1.6.tar.gz
Size 203.1 kB
Tags Source
SHA-256 checksum
How to use checksums
42cf5d572e0326552de58424a03b465b2f79ac84728225bf9cb534fa15bb6954
BLAKE2b-256 checksum
How to use checksums
abdb91abcc3fff41f4c61c89dcd76df8c66a08932cf319d8f6dc9d48a743ef9f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 19, 2026.

Transparency log

Release files / zndraw_socketio-0.1.6-py3-none-any.whl

Download URL zndraw_socketio-0.1.6-py3-none-any.whl
Size 20.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b4910b1f9e981dc085c3d3f457df4746638664335bf36f4ef64c488f3ec589d5
BLAKE2b-256 checksum
How to use checksums
acbdb781170790ecff50714833b2649417afa339594066cedf88dd0d0635801e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 19, 2026.

Transparency log

Security for zndraw-socketio 0.1.6

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for zndraw_socketio-0.1.6.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 19, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page